python

DjangoでassertDictEqualを使ってテストをスッキリさせよう

2020-07-20

Djangoでビューをテストする光景。

class MyViewTests(TestCase):
    def test_index(self):
        response = self.client.get(reverse('myapp:index'))
        self.assertEqual(response.status_code, 200)

        self.assertEqual(
            response.context['title'], 
            'タイトルテキスト 値')
        self.assertEqual(
            response.context['main_text'], 
            '本文の内容 値')
        self.assertEqual(
            response.context['name'], 
            '投稿者の名前 値')

このようにresponse.context['キー']を検証するごとにself.assertEqualを呼び出すのが当たり前の風潮ですよね。
assesrtEqualを何度も書くため、テストが長ったらしくなります。

そこで、assertDictEqualを使って期待する内容をごっそりテストしてしまうことにしました。

class MyViewTests(TestCase):
    def test_index(self):
        response = self.client.get(reverse('myapp:index'))
        self.assertEqual(response.status_code, 200)

        self.assertDictEqual(response.context.dicts[-1], {
            'title': 'タイトルテキスト 値',
            'main_text': '本文の内容 値',
            'name': '投稿者の名前 値'
        })

こんな感じで、response.context.dictsを直接参照すればテストがスッキリします。

もしテストに失敗しても、メッセージもわかりやすいです。

response.context.dictについて少し説明

response.context.dictsは、[ { } { } { } { } ]と言った構造をしています。
つまりdictを要素に持つlistです。

テストをデバッグしてresponse.context.dictの内容を確認してみると、

[
  {
    'True': True, 
    'False': False, 
    'None': None}, 
  {
    'csrf_token': <SimpleLazyObject: 'kDOsIyMr0qbxIbJ5AiROjBvZty1jftD7MMqgZo8q1bWZvRiXL1IiVCFB3rIXUsuZ'>, 
    'request': <WSGIRequest: GET '/grouper/'>, 
    'user': <SimpleLazyObject: <function AuthenticationMiddleware.process_request.<locals>.<lambda> at 0x1a74c5f90>>, 
    'perms': <django.contrib.auth.context_processors.PermWrapper object at 0x197662990>, 
    'messages': <django.contrib.messages.storage.fallback.FallbackStorage object at 0x10aabd650>, 
    'DEFAULT_MESSAGE_LEVELS': {'DEBUG': 10, 'INFO': 20, 'SUCCESS': 25, 'WARNING': 30, 'ERROR': 40}}, 
  {
  }, 
  {
    'title': 'タイトルテキスト 値', 
    'main_text': '本文の内容 値',
    'name': '投稿者の名前 値'
  }
]

といったようになっていて、ビューで設定した受け渡し値が一番最後の要素(response.context.dicts[-1])に入っています。

dictを要素にもつlistというのは、普通はあまり見ないですよね。
なぜこんなことになっているのかというと、response.context.dictsが settings.pyのcontext_processors で定義されているモジュールごとに個別に作られているから、という理由のようです。
Using RequestContext

デフォルトの設定ではsettings.pyで

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

このように、debug・request・auth・messagesモジュールが設定されていますが、ここにcontext_processorの設定を追加したりすると、response.context.dicts[-1]がテストしたいパラメータである保証はされなさそうですが…

その時はほとんどのテストが失敗することになること、テスト環境でしか影響が無いことなどから、採用リスクはほとんど無いと思います。

-python

© 2022 ヂまるBlog