Yur*_*nov 2 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
Run Code Online (Sandbox Code Playgroud)
问题:
如何避免在 TestCase 的每个测试中设置“return_value”的重复代码?
我期待“setUp”方法或类似的东西。
是否可以?
PS:模拟版mock==1.3.0,django版Django==1.8.4
您可以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
Run Code Online (Sandbox Code Playgroud)
(注意:当使用@mock.patch装饰器进行装饰时,是从下往上应用的,因此要mock_method_a作为第一个参数传入,您需要将装饰器放在最靠近类定义的位置)。
该return_value关键字参数mock.patch.object()传递给MagicMock()构造函数。请参阅mock.patch.object()文档:
像
patch(),patch.object()接受任意关键字参数来配置它创建的模拟对象。
Mock接受几个指定Mock对象行为的可选参数:
[...]
return_value: 调用模拟时返回的值。默认情况下,这是一个新的Mock(在第一次访问时创建)。查看return_value属性。
如果您还想避免在测试用例之外设置模拟或不喜欢每个测试函数的附加参数,那么您还可以在setUp方法中创建修补程序,然后在测试结束时通过注册将其再次删除通过unittest.TestCase.addCleanup()方法回调。
修补程序通过调用返回新模拟对象的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.
Run Code Online (Sandbox Code Playgroud)
请注意,该mock.patch.stopall()方法将停止所有已启动的模拟修补程序。您还可以传递.stop每个修补程序的属性:
self.addCleanup(patcher_method_a.stop)
self.addCleanup(patcher_method_b.stop)
self.addCleanup(patcher_method_c.stop)
Run Code Online (Sandbox Code Playgroud)
如果你必须创建很多这样的设置,你可以创建一个帮助函数来处理重复的部分:
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)
Run Code Online (Sandbox Code Playgroud)
并且可能在映射的循环中使用它:
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)
Run Code Online (Sandbox Code Playgroud)
所述patcher.start()在一个方法TestCase.setUp()的方法可以更容易地使用继承,其中碱测试用例情况下,使用为基础,多组测试,所有使用相同的共享嘲笑设置。