Django 测试 - 在所有测试中修补对象

问题描述 投票:0回答:4

我需要为我的测试创建某种

MockMixin
。它应该包括调用外部源的所有内容的模拟。 例如,每次我在管理面板中保存模型时,我都会调用一些远程 URL。如果能嘲笑它并像这样使用那就太好了:

class ExampleTestCase(MockedTestCase):
    # tests

因此,每次我在管理中保存模型时,例如在功能测试中,都会应用此模拟,而不是调用远程 URL。

这真的可能吗?我能够针对一项特定测试做到这一点,这不是问题。但拥有一些全局模拟会更有用,因为我经常使用它。

python django unit-testing django-testing python-mock
4个回答
65
投票

根据

mock
文档

Patch 可以用作 TestCase 类装饰器。它的工作原理是 装饰类中的每个测试方法。这减少了样板文件 当您的测试方法共享通用补丁集时编写代码。

这基本上意味着您可以创建一个基测试类,并在其上应用

@patch
装饰器,该类将模拟您的外部调用,同时执行内部的每个测试方法。

此外,您还可以分别在 start()

stop()
 方法中使用 
setUp()
tearDown()
 patcher 方法:

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

    def tearDown(self):
        self.patcher.stop()

38
投票

只是添加到alecxe的答案,如果您使用

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

6
投票

我最终创建了一个测试运行器来实现我的目的。我需要模拟文件存储,以便在测试时图像不会实际写入文件系统。在许多测试中都会调用图像对象,因此修补每个类不会是

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
投票

这不是一个 mixin,但是

patch
作为类装饰器也可以工作:

@patch("module.foo_function", lambda: "patched!")
class PatchedTestCase(TestCase):

    def test_1(self):
        self.assertEqual(foo_function(), "patched!")

    def test_2(self):
        self.assertEqual(foo_function(), "patched!")

我测试过,没有看到对其他测试类有任何副作用,我认为不需要添加

tearDown()
方法。

© www.soinside.com 2019 - 2024. All rights reserved.