简体   繁体   English

如何为TestCase设置一次“return_value”。 蟒蛇。 Django的

[英]How to set “return_value” once for TestCase. Python. Django

Here is example test: 这是示例测试:

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


Queston : 奎斯顿
How I can avoid of duplicate code for setting "return_value" in each test in TestCase? 如何避免在TestCase中的每个测试中设置“return_value”的重复代码?

I expect something in "setUp" method or something similar. 我期待“setUp”方法或类似的东西。
Is it possible? 可能吗?

PS: mock version mock==1.3.0, django version Django==1.8.4 PS:模拟版本模拟== 1.3.0,django版本Django == 1.8.4

You can set the return_value right there in the @mock.patch.object() decorators: 您可以在@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

(Note: when decorating with @mock.patch the decorators are applied from the bottom on up, so for mock_method_a to be passed in as the first argument you need to put the decorator closest to the class definition). (注意:使用@mock.patch进行装饰时,装饰器从底部开始应用,因此mock_method_a作为第一个参数传入,需要将装饰器放在最接近类定义的位置)。

The return_value keyword argument to mock.patch.object() is passed to the MagicMock() constructor. mock.patch.object()return_value关键字参数传递给MagicMock()构造函数。 See the mock.patch.object() documentation : 请参阅mock.patch.object()文档

Like patch() , patch.object() takes arbitrary keyword arguments for configuring the mock object it creates. patch()一样, patch.object()接受任意关键字参数来配置它创建的模拟对象。

and the mock.Mock documentation : mock.Mock文档

Mock takes several optional arguments that specify the behaviour of the Mock object: Mock采用几个可选参数来指定Mock对象的行为:

  • [...] [...]

  • return_value : The value returned when the mock is called. return_value :调用mock时返回的值。 By default this is a new Mock (created on first access). 默认情况下,这是一个新的Mock (在第一次访问时创建)。 See the return_value attribute. 请参阅return_value属性。

If you also want to avoid setting the mocks outside of your test case or don't like the additional arguments to each test function, then you you can also can create patchers in the setUp method, which then are removed again when the test ends by registering a callback via the unittest.TestCase.addCleanup() method . 如果您还想避免在测试用例之外设置模拟或者不喜欢每个测试函数的附加参数,那么您也可以在setUp方法中创建修补程序 ,然后在测试结束时再次删除它们通过unittest.TestCase.addCleanup()方法注册回调。

The patchers are applied for each test, by calling the patcher.start() methods , which returns the new mock object: 通过调用patcher.start()方法为每个测试应用patcher.start() ,该方法返回新的模拟对象:

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.

Note that the mock.patch.stopall() method will stop all mock patchers that have started . 请注意, mock.patch.stopall()方法将停止所有已启动的模拟修补程序 You can also pass the .stop attributes of each of the patchers: 您还可以传递每个.stop属性:

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

If you have to create a lot of such setups, you could create a helper function that'll take care of the repeated parts: 如果你必须创建很多这样的设置,你可以创建一个帮助函数来处理重复的部分:

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)

and perhaps use this in a loop over a mapping: 并且可能在映射循环中使用它:

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)

The patcher.start() approach in a TestCase.setUp() method makes it easier to use inheritance , where a base testcase case is used as the basis for several test cases that all use the same shared mocking setup. TestCase.setUp()方法中的patcher.start()方法使得继承更容易,其中基本测试用例用作所有使用相同共享模拟设置的几个测试用例的基础。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM