PyHamcrestを使って柔軟なunittestを書こう！

## はじめに
pythonのunittestでDict型の中にList型があって，テストをするたびに通ったり通らなかったりすることってありませんか？
私はあって悩んだのでここに書き残しておきます．
解決の糸口は[このサイト](https://stackoverflow.com/questions/14491164/python-unittest-asserting-dictionary-with-lists)(1)からいただきました．ありがとうございます．

## assertEqualで対応できないところ
[ここ](https://qiita.com/phorizon20/items/acb929772aaae4f52101)(2)を参考にしながら以下のようなテストを書きました．

```python
import unittest


class VTest(unittest.TestCase):

    maxDiff = None

    def setUp(self):
        pass

    def tearDown(self):
        pass

    def test_v(self):
         first_json = {
            'kind': 'voiceroid',
            'items': [
                {'name': 'aoi'},
                {'name': 'akane'},
                {'name': 'yukari'}
            ]
        }

        second_json = {
            'kind': 'voiceroid',
            'items': [
                {'name': 'yukari'},
                {'name': 'aoi'},
                {'name': 'akane'},
            ]
        }
        self.assertEqual(first_json, second_json)


if __name__ == "__main__":
    unittest.main()

```

この時itemsの順序はどうでもいいよ～中身さえあってればね・・・みたいな状況になったとします．なので，個人的にはこれはテストOKです．ですが実際に動かすと

```
F
======================================================================
FAIL: test_v (__main__.VTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".\v_test.py", line 34, in test_v
    self.assertEqual(first_json, second_json)
AssertionError: {'kin[20 chars]ems': [{'name': 'aoi'}, {'name': 'akane'}, {'name': 'yukari'}]} != {'kin[20 chars]ems': [{'name': 'yukari'}, {'name': 'aoi'}, {'name': 'akane'}]}
- {'items': [{'name': 'aoi'}, {'name': 'akane'}, {'name': 'yukari'}],
+ {'items': [{'name': 'yukari'}, {'name': 'aoi'}, {'name': 'akane'}],
   'kind': 'voiceroid'}

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=1)
```

こんな風に怒られちゃいます．
いやでも個人的には・・・・とか言ってもダメなものはダメです．というのが```assertEqual関数```です．
そこで，値は大事だけど順序はいらないという要望に柔軟に応えてくれるライブラリが[PyHamcrest](https://github.com/hamcrest/PyHamcrest)(3)です．
外部ライブラリなので

```
pip install PyHamcrest
```
でインストールしたのち先ほどのコードを以下のように書き換えます．

```python
from hamcrest import *
import unittest


class VTest(unittest.TestCase):

    maxDiff = None

    def setUp(self):
        pass

    def tearDown(self):
        pass

    def test_v(self):
        first_json = {
            'kind': 'voiceroid',
            'items': [
                {'name': 'aoi'},
                {'name': 'akane'},
                {'name': 'yukari'}
            ]
        }

        second_json = {
            'kind': 'voiceroid',
            'items': [
                {'name': 'yukari'},
                {'name': 'aoi'},
                {'name': 'akane'}
            ]
        }

        # self.assertEqual(first_json, second_json)
        assert_that(first_json, second_json)


if __name__ == "__main__":
    unittest.main()
```
これは```assertEqual関数```を```assert_that関数```に変えただけです．それではテストしてみると・・・

```
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
```
きれいに通りましたね．

## さいごに
要素は大事だけど順序はあんまり重要でないのに怒られて困ったーって方は一度試してみてはどうでしょうか？[PyHamcrestのチュートリアル](https://pyhamcrest.readthedocs.io/en/release-1.8/tutorial/)(4)はこちらですいろいろあるので他にも試してみてくださいきっとテストが柔軟になると思います！

## 参考
(1) [Python unittest - asserting dictionary with lists](https://stackoverflow.com/questions/14491164/python-unittest-asserting-dictionary-with-lists)
(2) [最短で試すPythonテストコード](https://qiita.com/phorizon20/items/acb929772aaae4f52101)
(3) [PyHamcrest](https://github.com/hamcrest/PyHamcrest)
(4) [PyHamcrest Tutorial](https://pyhamcrest.readthedocs.io/en/release-1.8/tutorial/)