假定以下设置:
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类的更新方法编写单元测试,以检查是否调用了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的更新方法。在这种情况下,如何测量B.update在A.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)
现在,更新函数实际上已经运行了3次(我在控制台输出中看到它,因为“被调用的更新。”打印了3次),但是更新方法的call_count保持为零。我在检查错误的属性/对象吗?
答案 0 :(得分:1)
如何为
TestA.test_update()
编写单元测试以查看是否调用了B.update()
?
这只是为了提供一些想法。
import mock
import unittest
import A
import B
class TestB(unittest.TestCase):
# only mock away update method of class B, this is python2 syntax
@mock.patch.object(B, 'update')
def test_update(self, mockb_update):
# B.update() does not return anything
mockb_update.return_value = None
nodes = {}
nodes['first'] = B(int_attr=1)
nodes['second'] = B(int_attr=2)
test_A = A(nodes)
test_A.update(bool_A=True)
self.assertTrue(mockb_update.called)
如何检查所有
B.update()
的所有A.nodes
是否被调用?
# same everthing except this
self.assertEqual(mockb_update.call_count, 2)
在OP更新代码后未嘲笑
B.is_udpated
时陷入无限循环
在B.is_updated
内模拟__init__
或模拟类__init__
是一个比原始帖子复杂的话题
这里有一些想法,B.is_updated
不能仅仅是mock.patch
,它只有在B类启动之后才可用。所以选择是
a)模拟B.__init__
或类构造函数
b)模拟整个类B
,在您的情况下更容易,将is_updated
设置为True
,将结束无限循环。
答案 1 :(得分:0)
结合@Gang和@jonrsharpe的答案,以下代码段解决了我在上面提出的问题:
如何为TestA.test_update()编写单元测试以查看是否调用了B.update()? 参见@Gangs的答案。
如何检查所有A.node的所有B.update()? 参见@Gangs的答案。
在OP更新代码后未模拟B.is_udpated时,将陷入无限循环
如@jonrsharpe所建议的,这里的解决方案是在节点中的每个B实例中创建一个模拟对象,并分别检查函数调用:
class TestA(TestCase):
@mock.patch('module.B')
@mock.patch('module.B')
@mock.patch('module.B')
def test_update(self, mock_B1, mock_B2, mock_B3):
nodes = {}
nodes['1'] = mock_B1
nodes['2'] = mock_B2
nodes['3'] = mock_B3
a = A(nodes)
a.update()
with self.subTest():
self.assertEqual(mock_B1.update.call_count, 1)
with self.subTest():
self.assertEqual(mock_B2.update.call_count, 1)
with self.subTest():
self.assertEqual(mock_B3.update.call_count, 1)
此外,如果您出于某种原因想要执行模拟函数(以防它们设置了一些会影响运行时的标志或变量),则可以编写这样的测试:
def test_fit_skip_ancestors_all(self):
nodes = {}
nodes['1'] = mock_B1
nodes['2'] = mock_B2
nodes['3'] = mock_B3
a = A(nodes)
with mock.patch.object(A.nodes['1'],'update',wraps=A.nodes['1'].update) as mock_B1, \
mock.patch.object(A.nodes['2'], 'update', wraps=A.nodes['2'].update) as mock_B2, \
mock.patch.object(A.nodes['3'], 'update', wraps=A.nodes['3'].update) as mock_B3:
a.update()
with self.subTest():
self.assertEqual(mock_B1.call_count, 1)
with self.subTest():
self.assertEqual(mock_B2.call_count, 1)
with self.subTest():
self.assertEqual(mock_B3.call_count, 1)