如何为 TestCase 设置一次 "return_value"。 Python。姜戈

How to set "return_value" once for TestCase. Python. Django

这是示例测试:

import a
import b
import c

import mock
from django.test import TestCase

@mock.patch.object(a, "method_a")
@mock.patch.object(b, "method_b")
@mock.patch.object(c, "method_c")
class SomeTestCase(TestCase):

    def setUp(self):
        # I want to set mock_method_a.return_value = 1 once here (or not here, but once)
        pass

    def test_one(self, mock_method_a, mock_method_b, mock_method_c):
        mock_method_a.return_value = 1
        mock_method_b.return_value = 2
        pass  # some test stuff

    def test_two(self, mock_method_a, mock_method_b, mock_method_c):
        mock_method_a.return_value = 1
        mock_method_b.return_value = 2
        pass  # some test stuff

    def test_three(self, mock_method_a, mock_method_b, mock_method_c):
        mock_method_a.return_value = 1
        mock_method_b.return_value = 2
        pass  # some test stuff


奎斯顿:
如何避免在 TestCase 的每个测试中设置 "return_value" 的重复代码?

我期待 "setUp" 方法或类似的东西。
可能吗?

PS: mock版本mock==1.3.0,django版本Django==1.8.4

您可以在 @mock.patch.object() 装饰器中设置 return_value

@mock.patch.object(c, "method_c", return_value=3)
@mock.patch.object(b, "method_b", return_value=2)
@mock.patch.object(a, "method_a", return_value=1)
class SomeTestCase(TestCase):
    def test_one(self, mock_method_a, mock_method_b, mock_method_c):
        # do test stuff, return values have been set

    def test_two(self, mock_method_a, mock_method_b, mock_method_c):
        # do test stuff, return values have been set

    def test_three(self, mock_method_a, mock_method_b, mock_method_c):
        # do test stuff, return values have been set

(注意:当使用 @mock.patch 装饰时,装饰器是从下往上应用的,因此要将 mock_method_a 作为第一个参数传入,您需要将装饰器放在最靠近class定义)。

mock.patch.object()return_value 关键字参数被传递给 MagicMock() 构造函数。见 mock.patch.object() documentation:

Like patch(), patch.object() takes arbitrary keyword arguments for configuring the mock object it creates.

mock.Mock documentation

Mock takes several optional arguments that specify the behaviour of the Mock object:

  • [...]

  • return_value: The value returned when the mock is called. By default this is a new Mock (created on first access). See the return_value attribute.

如果您还想避免在测试用例的 外部设置模拟 或者不喜欢每个测试函数的附加参数,那么您还可以创建 patcherssetUp 方法中,然后在测试结束时通过 unittest.TestCase.addCleanup() method.

注册回调再次删除

通过调用 the patcher.start() methods 为每个测试应用补丁程序,returns 新模拟对象:

class SomeTestCase(TestCase):    
    def setUp(self):
        patcher_method_a = mock.patch.object(a, "method_a")
        self.mock_method_a = patcher_method_a.start()
        self.mock_method_a.return_value = 1

        patcher_method_b = mock.patch.object(b, "method_b")
        self.mock_method_b = patcher_method_b.start()
        self.mock_method_b.return_value = 2

        patcher_method_c = mock.patch.object(c, "method_c")
        self.mock_method_c = patcher_method_c.start()
        self.mock_method_c.return_value = 3

        # when the test is done, stop **all** patchers
        self.addCleanup(mock.patch.stopall)

    def test_one(self):
        # use self.mock_method_a, etc.

    def test_two(self, mock_method_a, mock_method_b, mock_method_c):
        # use self.mock_method_a, etc.

    def test_three(self, mock_method_a, mock_method_b, mock_method_c):
        # use self.mock_method_a, etc.

请注意,mock.patch.stopall() 方法将停止所有已启动的模拟修补程序。您还可以传递每个修补程序的 .stop 属性:

self.addCleanup(patcher_method_a.stop)
self.addCleanup(patcher_method_b.stop)
self.addCleanup(patcher_method_c.stop)

如果您必须创建很多这样的设置,您可以创建一个辅助函数来处理重复的部分:

def setup_object_patch(testcase, object, target, return_value, attrname=None):
    patcher = mock.patch.object(object, target)
    mock = patcher.start()
    mock.return_value = return_value
    setattr(testcase, attrname or f'mock_{target}', mock)
    testcase.addCleanup(patcher.stop)

也许在映射循环中使用它:

def setUp(self):
    mocks = {
        # attribute name on test -> object, target, return_value
        'mock_method_a': (a, 'method_a', 1),
        'mock_method_b': (b, 'method_b', 2),
        'mock_method_c': (c, 'method_c', 3),
    }
    for attrname, params in mocks.items():
        setup_object_patch(*params, attrname=attrname)

TestCase.setUp()方法中的patcher.start()方法使得使用继承更容易,其中一个基本测试用例被用作多个测试的基础所有使用相同共享模拟设置的案例。