Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/288.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Python单元测试中正确检查MagicMock对象_Python_Unit Testing_Mocking_Magicmock - Fatal编程技术网

在Python单元测试中正确检查MagicMock对象

在Python单元测试中正确检查MagicMock对象,python,unit-testing,mocking,magicmock,Python,Unit Testing,Mocking,Magicmock,我正在测试此代码: def to_be_tested(x): return round((x.a + x.b).c()) 在我的unittest中,我想断言这是通过传递的x和返回的结果来完成的,因此我将MagicMock对象作为x传递: 然后我检查结果是否符合我的预期: self.assertEqual(r._mock_new_name, '()') # created by calling round_call = r._mock_new_parent self

我正在测试此代码:

def to_be_tested(x):
  return round((x.a + x.b).c())
在我的unittest中,我想断言这是通过传递的x和返回的结果来完成的,因此我将MagicMock对象作为x传递:

然后我检查结果是否符合我的预期:

    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函数。有关详细信息,请参阅:

unittest.mock._all_magics.add('__round__')
unittest.mock._magics.add('__round__')
所以,现在,正如我所说,这是可行的。但我觉得它非常不可读。此外,我还需要到处玩才能找到像“mock\u new\u parent”之类的东西。下划线还表示这是一个私有属性,不应该使用。文档中没有提到这一点。它也没有提到另一种实现我所尝试的方式


有没有更好的方法来测试返回的MagicMock对象是否以其本应的方式创建?

你太过分了。您正在测试实现,而不是结果。此外,您正在深入模拟实现的内部,而无需触及这些内部

测试您是否得到正确的结果,并测试结果是否基于您想要使用的输入。您可以设置模拟,以便向round传递一个实际的数值:

x、 a+x.b导致对m.a.的调用。添加,传入m.b。 m、 调用了..\uuuu add\uuuuuu.c,因此如果需要,我们可以测试是否调用了它。 只需将c的结果设置为一个数字,以进行四舍五入。从函数means.c获取正确的轮号结果被调用。 在这里传递一个数字进行舍入就足够了,因为您没有测试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调用assert传递已经证明至少发生了一个m.a+表达式,并且在结果上访问了c

如果必须验证在实际的模拟实例上使用了round,则必须坚持修补MagicMock类,将_round__作为特殊方法包括在内,并删除mock_c.return_值赋值,然后可以使用

# 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)
一些进一步的说明:

试图把所有东西都变成模拟对象是没有意义的。如果测试中的代码应该在标准Python类型上工作,只需让您的模拟生成这些类型即可。例如,如果某个调用预期会产生一个字符串,则让您的模拟返回一个测试字符串,特别是当您将内容传递给其他标准库API时。 mock是单态的。您不需要从给定的mock返回来测试它们是否具有正确的父对象,因为您可以通过遍历父属性然后使用is来访问同一对象。例如,如果函数在某处返回模拟对象,则可以通过测试assertIsmock_object.some.access.return_value.path,returned_object来断言返回了正确的模拟对象。 调用mock时,会记录该事实。您可以使用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'>)]

是的,但您的主张是,测试实现没有意义。并非所有情况下都是这样。如果您碰巧不得不重构一些代码,这些代码所做的事情没有人能清楚地理解,但是这些事情不能因为重构而改变,那么只需测试这些事情是否有效,而不理解为什么会有意义。我希望能在这种情况下使用MagicMocks。当然,在正常情况下,理解代码的功能并在更高级别上进行测试是非常有意义的。请检查结果的含义。@Alfe:如果.c结果确实是自定义对象,则需要对数值类型挂钩进行单独的测试。我仍然坚持上述测试;这不是测试round是否能在自定义类型上工作的地方。实际上,我只想确保使用我的something作为参数调用它,而不想弄清楚它是什么,实际上,只是为了能够编写测试用例。当然,测试回合不是这里的问题。@Alfe:所以上面的“某物”是mock_c.return_值设置为的值。我想要e。G让一个单元测试显示重构者用truncx+0.5或类似的方法代替了round调用。当然,这里的这些例子意义有限。我认为在我实际的不太像mcve的情况下,这将是一个MagicMock的有用使用。
>>> 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'>)]