【发布时间】:2018-08-30 16:07:48
【问题描述】:
假设如下设置:
class A:
def __init__(self, nodes):
self.nodes=nodes
def update(self, bool_a=True):
if bool_a:
for n in self.nodes:
if hasattr(self.nodes[n], 'update'):
self.nodes[n].update()
class B:
def __init__(self, int_attr=5):
self.int_attr=int_attr
def update(self):
self.int_attr = 0
让我们假设 A 类中的节点列表实际上是 B 类的实例列表。
如何为A类的update方法编写单元测试,检查A类的self.nodes中包含的每个B类节点的update方法是否被调用?
在更一般的设置中,让我们假设有多个类实现了更新方法,并且可以是类 A 的 self.nodes 中的节点。如何检查 self.nodes 成员的所有更新方法都被调用了?
我尝试了以下方法,但没有成功:
mock_obj = MagicMock()
@patch('module.A.update', return_value=mock_obj)
def test_update(self, mock_obj):
nodes = {}
nodes['first'] = B(int_attr=1)
nodes['second'] = B(int_attr=2)
test_A = module.A(nodes=nodes)
test_A.update(bool_A=True)
self.assertTrue(mock_obj.called)
如mocking a function within a class method 建议的那样。
编辑:如果我们假设这种特殊情况:
import unittest
import mock
from unittest import TestCase
class A:
def __init__(self, nodes):
self.nodes=nodes
def update(self, bool_a=True):
if bool_a:
to_update = [n for n in self.nodes]
while len(to_update) > 0:
if hasattr(self.nodes[to_update[-1]], 'update'):
self.nodes[to_update[-1]].update()
print('Update called.')
if self.nodes[to_update[-1]].is_updated:
to_update.pop()
class B:
def __init__(self, int_attr=5):
self.int_attr=int_attr
self.is_updated = False
def update(self):
self.int_attr = 0
self.is_updated = True
class TestEnsemble(TestCase):
def setUp(self):
self.b1 = B(1)
self.b2 = B(2)
self.b3 = B(3)
self.nodes = {}
self.nodes['1'] = self.b1
self.nodes['2'] = self.b2
self.nodes['3'] = self.b3
self.a = A(self.nodes)
@mock.patch('module.B.update')
def test_update(self, mock_update):
mock_update.return_value = None
self.a.update()
with self.subTest():
self.assertEqual(mock_update.call_count, 3)
在这种情况下运行 unittest 会导致无限循环,因为 is_updated 属性永远不会设置为 True,因为 B 类的更新方法是模拟的。在这种情况下,如何测量在 A.update 中调用 B.update 的时间量?
更新: 试过这个:
@mock.patch('dummy_script.B')
def test_update(self, mock_B):
self.a.update()
with self.subTest():
self.assertEqual(mock_B.update.call_count, 3)
update 函数现在确实运行了 3 次(我在控制台输出中看到它,因为“Update called.”被打印了 3 次),但是 update 方法的 call_count 保持为零。我是否检查了错误的属性/对象?
【问题讨论】:
-
你永远不应该嘲笑你真正想要测试的东西,那是完全倒退的。你为什么不建立一个模拟 B 的列表并传递它呢?另请注意stackoverflow.com/q/1132941/3001761 和stackoverflow.com/q/240178/3001761,您的测试和生产代码中的可变对象存在重大问题。
-
好吧,我这里只用了一个列表作为例子,我同意这并不理想。实际上,它无论如何都是字典(也是可变的)。我更新了问题。
-
然后制作一个mock字典并传入;思路是一样的,这里完全不需要打补丁。
-
您介意帮我写一个短代码 sn-p 吗?如您所见,我对单元测试很陌生...:/抱歉打扰:)
标签: python python-3.x unit-testing mocking