31:過剰なmockを避ける

mock は便利なライブラリですが、使いすぎには要注意です。

mockでよくある失敗から、ベストプラクティスを学びましょう。

具体的な失敗

DjangoのView関数をテスト対象として考えます。

from .forms import PostSearchForm
from .posts import search_posts


def post_list(request):
    if request.GET:
        form = PostSearchForm(request.GET)
        if form.is_valid():
            text = form.cleaned_data['search']
            posts = search_posts(text)
    else:
        form = PostSearchForm()
        posts = Post.objects.all()
    return TemplateResponse(request, 'post_list.html',
                            {'posts': posts, 'form': form})

このView関数のテストとして mock を使いすぎると、次のようになります。

from unittest import mock
from django.test import TestCase

class TestPostList(TestCase):
    @mock.patch('posts.search_posts',
                return_value=[{'title', 'タイトル', 'body': '本文'}])
    @mock.patch('forms.PostSearchForm')
    def test_search(self, m_search, m_form):
        with mock.patch.object(m_form, 'is_valid', return_value=True):
            res = self.client.get('/posts', data={'search': '本文'})

        assert '本文' in res.content.decode()

この例ではView関数の動作のみをテストするためにmockを乱用しています。 しかし、このテストから確認できることは、ほぼありません。 search=本文 のように指定されたクエリーパラメーターが正しくフォームで解釈されて、検索に使われて、テンプレートに描画されるつながりを確認できないからです。

ベストプラクティス

mockを使いすぎるよりも、単純にデータを作成して動作確認をするほうが良いでしょう。

class TestPostList(TestCase):
    def test_search(self):
        PostFactory(title='タイトル')
        PostFactory(title='テスト')

        res = self.client.get('/posts', data={'search': 'タイトル'})

        assert "タイトル" in res.content.decode()
        assert "テスト" not in res.content.decode()

cover

(中略)詳細は書籍 自走プログラマー をご参照ください

関連