在单元测试中覆盖 python 函数局部变量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28688057/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 03:36:49  来源:igfitidea点击:

override python function-local variable in unittest

pythonmockingunit-testing

提问by anregen

I have a method in python (2.7) that does foo, and gives up after 5 minutes if foo didn't work.

我在 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')

I know some possible results from foo(), so I am using mock to fake those return values. The problem is, I don't want the test to run 5 minutes before it gets to see the exception.

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

Is there a way to override that local value of timeout? I'd like that to be just a few seconds so that I can see the loop try a couple times, then give up and raise.

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

The following does not work:

以下不起作用:

@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
    ...

采纳答案by mgilson

Rather than trying to mock the value if timeout, you'll want to mock the return value of time.time().

与其尝试模拟 if 的值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])
    ...

Now the first time time.time()is called, you'll get the value of 100, so it should timeout when after a few turns of your while loop. You can also mock time.sleepand just count how many times it gets called to make sure that part of the code is working properly.

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



Another approach (which isn't completely orthogonal to the one above) is to allow the user to pass an optional timeout keyword to the function:

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

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

This allows you to specify whatever timeout you want in the tests (and in future code which doesn't want to wait 5 minutes ;-).

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

回答by Alex Martelli

You can't mock a function's local variable. To make your code easier to test, change it to, e.g:

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

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

so it becomes trivial for tests to run it with a shorter timeout!

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