模拟一个在方法调用后更改实例变量的python类?

时间:2014-09-12 20:31:56

标签: python unit-testing mocking

我有一些代码,其中对方法update()的调用会更改某些实例变量的值。我使用更改的值来保留循环。以下是我的代码的简化示例:

def do_stuff(self):
    # get a new instance of A
    a = get_a()
    while True:
        a.update()
        if a.state == 'state':
            break

这个类的简单版本(我无法更改类,因为它是第三方库):

class A(object):
    def __init__(self):
        self.state = ''

    def update(self):
        # call to external system
        self.state = extern_func()

现在我想通过模拟类A来测试我的函数do_stuff()。为了测试函数的每个方面,我希望拥有state的所有不同值,并且每次调用{ {1}}(迭代不同的状态)。

我开始使用这个设置进行单元测试:

a.update()

我可以使用from mock import Mock, patch import unittest class TestClass(unittest.TestClass): @patch('get_a') def test_do_stuff(self, mock_get_a): mock_a = Mock(spec=A) mock_get_a.return_value = mock_a # do some assertions 实现这种行为吗? 我知道MockMock为连续的函数调用返回不同的值。但是我无法找到一种在函数调用后更改实例变量值的方法吗?

1 个答案:

答案 0 :(得分:1)

设定:

from mock import Mock, MagicMock, patch

sep = '***********************\n'

# non-mock mocks
def get_a():
    return A()
def extern_func():
    return 'foo'

def do_stuff(self):
    # get a new instance of A
    a = get_a()
    while True:
        a.update()
        print a.state
        if a.state == 'state':
            break

class A(object):
    def __init__(self):
        self.state = ''

    def update(self):
        # call to external system
        self.state = extern_func()

正如@Simeon Viser所提到的,嘲弄extern_func会起作用:

print sep, 'patch extern'
mock = MagicMock(side_effect = [1,2,3,4, 'state'])
@patch('__main__.extern_func', mock)
def foo():
    do_stuff(3)
foo()

>>> 
***********************
patch extern
1
2
3
4
state

side_effect可以是一个函数,您可以使用A.update参数模拟未绑定的方法auto_spec = True

使用上下文管理器:

print sep, 'patch update context manager call do_stuff'
def update_mock(self):
    self.state = mock()
mock = MagicMock(side_effect = [1,2,3,4, 'state'])
with patch.object(A, 'update', autospec = True) as mock_update:
    mock_update.side_effect = update_mock
    do_stuff(3)

>>>
***********************
patch update context manager call do_stuff
1
2
3
4
state

使用装饰器:

print sep, 'patch update decorator test_do_stuff'
def update_mock(self):
    self.state = mock()
mock = MagicMock(side_effect = [1,2,3,4, 'state'])
@patch.object(A, 'update', autospec = True, side_effect = update_mock)
def test_do_stuff(self):
    do_stuff(3)
test_do_stuff()

>>>
***********************
patch update decorator test_do_stuff
1
2
3
4
state

警告:我从未编写过全面的单元测试,并且最近才开始阅读模拟文档,所以即使我似乎完成了这项工作,我也无法评论它在测试中的效果方案。编辑欢迎。