web-dev-qa-db-ja.com

Django tests-すべてのテストのパッチオブジェクト

テスト用に何らかのMockMixinを作成する必要があります。外部ソースを呼び出すすべてのもののモックを含める必要があります。たとえば、管理パネルでモデルを保存するたびに、いくつかのリモートURLを呼び出します。それをあざけってそのように使うのは良いことです:

class ExampleTestCase(MockedTestCase):
    # tests

したがって、機能テストなどの管理でモデルを保存するたびに、リモートURLを呼び出す代わりにこのモックが適用されます。

それは実際に可能ですか?私は1つの特定のテストでそれを行うことができますが、それは問題ではありません。しかし、私はそれを頻繁に使用するので、いくつかのグローバルなモックがある方が便利でしょう。

29
tunarob

mock documentation によれば:

パッチは、TestCaseクラスデコレータとして使用できます。クラス内の各テストメソッドを装飾することで機能します。これにより、テストメソッドが共通のパッチセットを共有する場合のボイラープレートコードが削減されます。

これは基本的に、内部のすべてのテストメソッドが実行されている間に外部呼び出しを模擬する_@patch_デコレータが適用された基本テストクラスを作成できることを意味します。

また、 start()およびstop() パッチャーのメソッドをそれぞれsetUp()およびtearDown()メソッドで使用できます:

_class BaseTestCase(TestCase):
    def setUp(self):
        self.patcher = patch('mymodule.foo')
        self.mock_foo = self.patcher.start()

    def tearDown(self):
        self.patcher.stop()
_
40
alecxe

alecxe's answer に追加するだけですが、teardown()を使用している場合は ドキュメントに従って

stopを呼び出して、パッチが「取り消され」ていることを確認する必要があります。 setUpで例外が発生した場合、tearDownが呼び出されないため、これは想像以上に手間がかかる可能性があります。

テストで例外が発生した場合、パッチは元に戻されません。より良い方法は、addCleanup()内で setUp() を呼び出すことです。次に、tearDown()メソッドを完全に省略できます。

class BaseTestCase(TestCase):
    def setUp(self):
        self.patcher = patch('mymodule.foo')
        self.mock_foo = self.patcher.start()
        self.addCleanup(self.patcher.stop) # add this line
22
Meistro

結局、目的を果たすためのテストランナーを作成しました。テスト中にイメージが実際にファイルシステムに書き込まれないように、ファイルストレージをモックする必要がありました。画像オブジェクトは多くのテストで呼び出されているため、各クラスにパッチを適用することはDRYではありません。また、テストが失敗した場合に備えて、ファイル自体をモックするとシステム上に残ることに気づきました。しかし、この方法はそうではありませんでした。

プロジェクトルートにrunner.pyファイルを作成しました

# runner.py
from unittest.mock import patch

from Django.test.runner import DiscoverRunner

from myapp.factories import ImageFactory


class UnitTestRunner(DiscoverRunner):

    @patch('Django.core.files.storage.FileSystemStorage.save')
    def run_tests(self, test_labels, mock_save, extra_tests=None, **kwargs):
        mock_save.return_value = ImageFactory.get_image()
        return super().run_tests(test_labels, extra_tests=None, **kwargs)

次に、python manage.py tests --testrunner=runner.UnitTestRunnerを使用してテストを実行します


わかりやすくするために、ImageFactory.get_imageメソッドはカスタムメソッドです

from Django.core.files.base import ContentFile
from factory.Django import DjangoModelFactory
from io import BytesIO
from PIL import Image as PilImage
from random import randint

class ImageFactory(DjangoModelFactory):

    @classmethod
    def get_image(cls, name='trial', extension='png', size=None):
        if size is None:
            width = randint(20, 1000)
            height = randint(20, 1000)
            size = (width, height)

        color = (256, 0, 0)

        file_obj = BytesIO()
        image = PilImage.new("RGBA", size=size, color=color)
        image.save(file_obj, extension)
        file_obj.seek(0)
        return ContentFile(file_obj.read(), f'{name}.{extension}')
0
giantas