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

时间:2018-08-06 15:06:24

标签: python django unit-testing mocking python-unittest

这是示例测试:

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:模拟版本模拟== 1.3.0,Django版本Django == 1.8.4

1 个答案:

答案 0 :(得分:2)

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

@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作为第一个参数传递,您需要使装饰器最接近类定义)。

return_value的{​​{1}}关键字参数传递给mock.patch.object()构造函数。参见mock.patch.object() documentation

  

就像MagicMock()一样,patch()可以使用任意关键字参数来配置它创建的模拟对象。

mock.Mock documentation

  

patch.object()带有几个可选参数,用于指定Mock对象的行为:

     
      
  • [...]

  •   
  • Mock:调用模拟时返回的值。默认情况下,这是一个新的return_value(在首次访问时创建)。请参阅Mock属性。

  •   

如果您还想避免在测试用例的外部设置 ,或者不喜欢每个测试函数的附加参数,那么还可以创建 patchers return_value方法中的em>,然后在测试结束时通过通过unittest.TestCase.addCleanup() method注册回调来再次将其删除。

通过调用the patcher.start() methods,将修补程序应用于每个测试,该测试将返回新的模拟对象:

setUp

请注意,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) 方法中的patcher.start()方法使使用继承更加容易,在该方法中,基本测试用例被用作多个测试用例的基础。相同的共享模拟设置。