在unittest中覆盖python函数局部变量

时间:2015-02-24 04:16:10

标签: python mocking unit-testing

我在python(2.7)中有一个foo方法,如果foo没有工作,我会在5分钟后放弃。

def keep_trying(self):
    timeout = 300  #empirically derived, appropriate timeout
    end_time = time.time() + timeout
    while (time.time() < end_time):
        result = self.foo()
        if (result == 'success'):
            break
        time.sleep(2)
    else:
        raise MyException('useful msg here')

我知道来自foo()的一些可能的结果,所以我使用mock伪造那些返回值。问题是,我不希望测试在看到异常之前运行5分钟。

有没有办法覆盖超时的本地值?我想这只是几秒钟,所以我可以看到循环尝试几次,然后放弃并加注。

以下不起作用:

@patch.object(myClass.keep_trying, 'timeout')
@patch.object(myClass, 'foo')
def test_keep_trying(self, mock_foo, mock_timeout):
    mock_foo.return_value = 'failed'
    mock_timeout.return_value = 10 # raises AttributeError
    mock_timeout = 10 # raises AttributeError
    ...

2 个答案:

答案 0 :(得分:15)

不是试图模拟timeout的值,而是要模拟time.time()的返回值。

e.g。

@patch.object(time, 'time')
def test_keep_trying(self, mock_time):
    mock_time.side_effect = iter([100, 200, 300, 400, 500, 600, 700, 800])
    ...

现在第一次调用time.time()时,你会得到100的值,所以它会在你的while循环几圈之后超时。您还可以模拟time.sleep并计算调用它的次数,以确保部分代码正常工作。


另一种方法(与上面的方法不完全正交)是允许用户将可选的timeout关键字传递给函数:

def keep_trying(self, timeout=300):
    ...

这允许您在测试中指定您想要的任何超时(以及将来不想等待5分钟的代码; - )。

答案 1 :(得分:14)

您无法模拟函数的局部变量。为了使代码更容易测试,请将其更改为,例如:

def keep_trying(self, timeout=300):
    end_time = time.time() + timeout
    # etc, as above

因此,测试以较短的超时运行它会变得微不足道!