我正在测试此代码:
def to_be_tested(x):
return round((x.a + x.b).c())
在我的unittest中,我想声明确切地说这是通过传递的x
并返回结果,所以我将MagicMock
对象作为x
传递:
class Test_X(unittest.TestCase):
def test_x(self):
m = unittest.mock.MagicMock()
r = to_be_tested(m)
然后我检查结果是否符合我的期望:
self.assertEqual(r._mock_new_name, '()') # created by calling
round_call = r._mock_new_parent
self.assertEqual(round_call._mock_new_name, '__round__')
c_result = round_call._mock_new_parent
self.assertEqual(c_result._mock_new_name, '()') # created by calling
c_call = c_result._mock_new_parent
self.assertEqual(c_call._mock_new_name, 'c')
add_result = c_call._mock_new_parent
self.assertEqual(add_result._mock_new_name, '()') # created by calling
add_call = add_result._mock_new_parent
self.assertEqual(add_call._mock_new_name, '__add__')
a_attribute = add_call._mock_new_parent
b_attribute = add_call.call_args[0][0]
self.assertEqual(a_attribute._mock_new_name, 'a')
self.assertEqual(b_attribute._mock_new_name, 'b')
self.assertIs(a_attribute._mock_new_parent, m)
self.assertIs(b_attribute._mock_new_parent, m)
导入unittest.mock
后,我需要修补mock
模块的内部结构,以便能够正确地模仿round()
函数(请参阅https://stackoverflow.com/a/50329607/1281485详细信息):
unittest.mock._all_magics.add('__round__')
unittest.mock._magics.add('__round__')
所以,现在,正如我所说,这是有效的。但我发现它非常难以理解。此外,我需要玩很多东西才能找到_mock_new_parent
等内容。下划线也表明这是一个私密的"属性,不应该使用。文档没有提到它。它也没有提到实现我尝试的另一种方式。
有没有更好的方法来测试返回的MagicMock
对象是否按原样进行创建?
答案 0 :(得分:1)
你太过火了。您正在测试实现,而不是结果。此外,您正在进入不需要触摸的模拟实现的内部。
测试您是否获得了正确的结果,并测试结果是否基于您要使用的输入。您可以设置模拟,以便round()
将实际数值传递给round:
x.a + x.b
调用m.a.__add__
,传入m.b
。m.a.__add__().c()
被调用,因此如果需要,我们可以测试它被调用。c()
的结果设置为round()
的数字即可完成。从函数中获取正确的round(number)
结果意味着.c()
被调用。将数字传递到round()
就足够了,因为您没有测试round()
函数。您可以依靠Python维护人员来测试该功能,专注于测试您自己的代码。
这就是我要测试的:
m = unittest.mock.MagicMock()
# set a return value for (x.a + *something*).c()
mock_c = m.a.__add__.return_value.c
mock_c.return_value = 42.4
r = to_be_tested(m)
mock_c.assert_called_once()
self.assertEqual(r, 42)
如果您必须断言m.a + m.b
已发生,那么您可以添加
m.a.__add__.assert_called_once(m.b)
但是mock_c
调用断言传递已经证明至少发生了(m.a + <whatever>)
表达式并且在结果上访问了c
。
如果您必须验证在实际模拟实例上使用round()
,您必须坚持修补MagicMock
类以包含__round__
作为一种特殊的方法并删除mock_c.return_value
赋值,之后你可以断言返回值是正确的对象
# assert that the result of the `.c()` call has been passed to the
# round() function (which returns the result of `.__round__()`).
self.assertIs(r, mock_c.return_value.__round__.return_value)
进一步说明:
is
。例如。如果函数在某处返回模拟对象,则可以通过测试assertIs(mock_object.some.access.return_value.path, returned_object)
断言 right 模拟对象已返回。assert_called*
方法,.called
和.call_count
属性断言,并使用.return_value
属性遍历调用结果如有疑问,请检查.mock_calls
属性以查看被测代码的访问内容。或者在交互式会话中执行此操作。例如,通过以下方式更快地查看m.a + m.b
在快速测试中的作用:
>>> from unittest import mock
>>> m = mock.MagicMock()
>>> m.a + m.b
<MagicMock name='mock.a.__add__()' id='4495452648'>
>>> m.mock_calls
[call.a.__add__(<MagicMock name='mock.b' id='4495427568'>)]