模拟副作用只有 X 次 [英] Mock side effect only X number of times

查看:48
本文介绍了模拟副作用只有 X 次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个芹菜重试任务,我想测试它是否重试直到成功.使用 mock 的 side_effect,我可以在一定数量的执行中失败,然后通过 None,清除副作用.但是,任务调用的方法在那个时候不会执行,它只是没有异常.有没有办法清除副作用,并且仍然让被模拟的方法正常执行?

I have a celery retry task that I would like to test that it retries until successful. Using mock's side_effect, I can fail it for a set number of executions and then passing None, clear the side effect. However, the method the task is calling doesn't execute at that point, it just doesn't have an exception. Is there a way to clear the side effect, and still have the method being mocked execute as normal?

我可以测试它被称为x"次(即重复直到成功),然后在单独的测试中,断言它做了应该做的事情,但想知道是否有办法做到这两者一个测试.

I can test that it is called 'x' number of times (ie. repeat until successful) and then in a separate test, assert it does what is supposed to, but was wondering if there was a way to do both in one test.

tasks.py:

import celery

@celery.task(max_retries=None)
def task():
    print "HERE"
    try:
        do_something("TASK")
    except Exception as exc:
        print exc
        raise task.retry(exc=exc)

def do_something(msg):
    print msg

测试:

import ....

class TaskTests(test.TestCase):

    @mock.patch('tasks.do_something')
    def test_will_retry_until_successful(self, action):
        action.side_effect = [Exception("First"), Exception("Second"), Exception("Third"), None]
        tasks.task.delay()
        self.assert.... [stuff about task]

结果:失败 3 次,然后成功"但 do_something() 从不打印.action.call_count 等于 4.我希望看到最后一个HERE"后面的空行将打印TASK".

Results: fails three times and then "succeeds" but do_something() never prints. action.call_count equals 4. I would like to see that the blank line following the last 'HERE' would be print of 'TASK'.

-------------------- >> begin captured stdout << ---------------------
HERE
First
HERE
Second
HERE
Third
HERE

--------------------- >> end captured stdout << ----------------------

推荐答案

嘲笑了 do_something().模拟完全取代了原来的;您的选择是产生副作用(从迭代中提高或返回一个值)或应用正常的模拟操作(返回一个新的模拟对象).

You mocked do_something(). A mock replaces the original entirely; your choices are to either have the side effect (raise or return a value from the iterable) or to have the normal mock operations apply (returning a new mock object).

此外,在 side_effect 序列中添加 None 不会重置副作用,它只是指示模拟返回值 None 代替.您可以添加 mock.DEFAULT 代替;在这种情况下,正常的模拟操作适用(就像调用模拟时没有副作用一样):

In addition, adding None to the side_effect sequence doesn't reset the side effect, it merely instructs the mock to return the value None instead. You could add in mock.DEFAULT instead; in that case the normal mock actions apply (as if the mock had been called without a side effect):

@mock.patch('tasks.do_something')
def test_will_retry_until_successful(self, action):
    action.side_effect = [Exception("First"), Exception("Second"), Exception("Third"), mock.DEFAULT]
    tasks.task.delay()
    self.assert.... [stuff about task]

如果您觉得您的测试必须以调用原始函数结束,则必须存储对原始未修补函数的引用,然后将 side_effect 设置为callable 会在时机成熟时转身并调用原件:

If you feel your test must end with calling the original, you'll have to store a reference to the original, unpatched function, then set the side_effect to a callable that will turn around and call the original when the time comes:

# reference to original, global to the test module that won't be patched
from tasks import do_something

class TaskTests(test.TestCase):
    @mock.patch('tasks.do_something')
    def test_will_retry_until_successful(self, action):
        exceptions = iter([Exception("First"), Exception("Second"), Exception("Third")])
        def side_effect(*args, **kwargs):
            try:
                raise next(exceptions)
            except StopIteration:
                # raised all exceptions, call original
                return do_something(*args, **kwargs)
        action.side_effect = side_effect
        tasks.task.delay()
        self.assert.... [stuff about task]

但是,我无法预见您想要这样做的单元测试场景.do_something() 不是被测试的 Celery 任务的一部分,它是一个外部单元,所以你通常应该只测试它是否被正确调用(使用正确的参数),以及正确的调用次数.

I cannot, however, foresee a unittesting scenario where you'd want to do that. do_something() is not part of the Celery task being tested, it is an external unit, so you should normally only test if it was called correctly (with the right arguments), and the correct number of times.

这篇关于模拟副作用只有 X 次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆