如何指导魔术模拟如何处理其参数

阿恩

我遇到了以下情况(边缘?),我不知道如何正确处理。普遍的问题是

  • 我有一个要测试的功能
  • 在该函数中,我以生成器理解为参数调用外部函数
  • 在测试中,我嘲笑了外部函数
  • 现在prod代码和经过测试的代码有所不同:在prod中,生成器已被消耗,模拟未执行该操作

这是我的代码库中的简化示例:

import itertools
import random


def my_side_effects():
    # imaginge itertools.accumulate was some expensive strange function
    # that consumes an iterable
    itertools.accumulate(random.randint(1, 5) for _ in range(10))


def test_my_side_effects(mocker):
    my_mocked_func = mocker.patch('itertools.accumulate')

    my_side_effects()

    # make sure that side-effects took place. can't do much else.
    assert my_mocked_func.call_count == 1

该测试运行得很好,足以满足我的所有需求。但是当我运行coverage代码时,我在摘要中描述的情况就显而易见了:

----------- coverage: platform linux, python 3.8.0-final-0 -----------
Name                                   Stmts   Miss Branch BrPart  Cover   Missing
----------------------------------------------------------------------------------
[...]
my_test_case.py                            5      0      2      1    86%   6->exit
[...]
----------------------------------------------------------------------------------
# something like this, the ->exit part on the external call is the relevant part

->exitcoverage.py中语法的说明鉴于理解能力可以执行我实际上确实想运行的相关业务逻辑,因此错过的覆盖范围很重要。它只是random.randint在这里打电话,但它可以做任何事情。


解决方法:

  1. 我可以只使用列表理解。代码被调用,每个人都很高兴。除了我,谁必须修改后端以简化测试。
  2. 在测试期间,我可以进入模拟程序,抓住呼叫arg,然后手动展开。这可能看起来很糟糕。
  3. 我可以使用猴子补丁功能代替使用Magicmock,类似这样的monkeypatch.setattr('itertools.accumulate', lambda x: [*x])描述。但是我将失去像我的示例那样做出呼叫断言的能力。

我认为一个好的解决方案是这样的,但遗憾的是不存在:

def test_my_side_effects(mocker):
    my_mocked_func = mocker.patch('itertools.accumulate')

    # could also take "await", and assign treatments by keyword
    my_mocked_func.arg_treatment('unroll')  

    my_side_effects()

    # make sure that side-effects took place. can't do much else.
    assert my_mocked_func.call_count == 1
威姆

您是正确的,这里缺少覆盖范围:实际上,由于从未使用过累加器,因此您甚至可以:

itertools.accumulate(ERRORERRORERROR for _ in range(10))

而且您现有的测试仍会通过(明显的错误刚刚被模拟)。

要解决此问题,请使用side_effect的模拟:

my_mocked_func = mocker.patch('itertools.accumulate', side_effect=list)

当使用可调用对象作为模拟对象时side_effect,它会使用与该模拟对象相同的参数进行调用,并且该可调用对象的返回值将用作该模拟对象的返回值(注意:这意味着您还可以在此处声明返回值而不是直言不讳的call_count断言)。

这将使您能够消耗发电机并在此处获得100%的覆盖率。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章