通过在各个代码修订版之间声明一致的行为来进行Python测试?

时间:2019-02-02 22:42:32

标签: python unit-testing tox

典型的Python单元测试断言某些函数返回了预先计算的输出值,例如:

def average(a, b):
    return (a + b) / 2

def test_average():
    assert average(2, 4) == 3

断言测试都遵循相同的基本公式:提供输入,提供期望的输出,调用要测试的代码,进行比较。我经常编写这样的测试,我认为它们在许多情况下的价值是显而易见的。

但是我对歧义性测试框架感到不安,因为它模糊了要如何准确地声明确定的结果(在示例中为3)。在测试中,您通常希望使用各种参数来展示边缘情况的行为,例如:

def test_average(a, b):
    params = [
        [1, 3, 2],
        [9000.5, 9000.5, 9000.5], 
        [100, -100, 0],
        [-1, 3, 1],
        [0, 0, 0]
    ]
    for a, b, result in params:
        assert average(a, b) == result

但是,如果您的参数列表很长或结果难以计算,则必须在两个选项之间进行选择:

  1. 手动运行一次被测代码,例如从IPython中获取,并将其返回值保留在代码库中作为测试断言
  2. 重新实现足够的被测代码,以便在测试用例中计算出预期结果

大多数开发人员使用两种方法的组合。但是,它们都没有给您太多证据证明该代码是正确,而只是在整个代码修订版中是一致的。运行测试实际上只是测试代码的行为与编写测试时的行为相同。

这让我想知道是否存在明确记录/比较跨修订版(针对被测软件)的运行时行为的测试实践。例如,您可以将“演示案例”返回值的JSON集合写入磁盘和版本控制,因此测试案例可以断言关于这些结果在各个版本之间的一致性的属性:

def demo_average():
    arg_a_vals = [0, -4.2, 1/12] + range(-100, 1000, 5)
    arg_b_vals = [0, -10**200, math.pi] + range(2**20, 2**20 + 17)
    return [
        a, b, average(a, b)
        for a in arg_a_vals
        for b in arg_b_vals
    ]

def save_demo(results):
    with open(f"demo_v{__version__}.json", "w") as f:
        json.dump(results, f)

def load_demo(version):
    with open(f"demo_v{version}.json") as f:
        return json.load(f)

def test_average():
    result = demo_average()
    save_demo(result)
    saved_result = load_demo(GOOD_PRIOR_VERSION)
    assert_lists_are_equal(result, saved_result)

此示例有意简单,但是可以save_demoload_demotest_average进行概括,因此在每个测试用例中唯一需要重复的代码是{{1 }} —可能输入值的枚举,其输出应在各个修订版之间进行比较。请注意,与普通的断言测试不同,代码不包含任何预期的输出值作为文字。

如果测试框架知道版本控制系统并且可以在多个克隆版本上运行演示以生成上面的demo_average对象,则还可以避免提交“演示”结果(可能非常大)的飞行。

是否有任何可以像这样工作的Python测试框架或项目?即通过验证代码在代码修订版中返回的一致结果来测试软件,而不是在测试用例中声明特定的预期结果。 [编辑:德克·赫尔曼(Dirk Hermann)在评论中指出,这有时称为背对背测试。]

请注意,我已经研究了tox,但不确定它能解决此用例。它支持跨版本比较,但是文档主要考虑确保您自己的现有测试用例通过项目依赖项的多个版本,我认为这不适用。

一些人也提到了Hypothesis。假设很有趣,并且与普通测试的工作原理也大不相同-简而言之,您将测试编写为高度参数化/广义的断言,假设就做聪明的事情来探索失败的参数空间。但是通过阅读文档,我看不到这如何适用于我的案例。我的目标是编写断言行为版本间一致性的测试,而不是单独检查单个代码版本可能断言的任何属性。

0 个答案:

没有答案