使用Python unittest测试可以返回非确定性结果的函数

Pau*_*ore 5 python testing unit-testing

我正在用Python编写一个小型的作业调度程序.调度程序可以被赋予一系列callables和依赖项,并且应该运行callables,确保在任何前任之前没有任务运行.

我试图遵循测试驱动的方法,我遇到了测试依赖项处理的问题.我的测试代码如下所示:

def test_add_dependency(self):
    """Tasks can be added with dependencies"""
    # TODO: Unreliable test, may work sometimes because by default, task
    #       running order is indeterminate.
    self.done = []
    def test(id):
        self.done.append("Test " + id)
    s = Schedule()
    tA = Task("Test A", partial(test, "A"))
    tB = Task("Test B", partial(test, "B"))
    s.add_task(tA)
    s.add_task(tB)
    s.add_dependency(tA, tB)
    s.run()
    self.assertEqual(self.done, ["Test B", "Test A"])
Run Code Online (Sandbox Code Playgroud)

问题是这个测试(有时)甚至在我添加依赖项处理代码之前就已经有效了.这是因为规范没有说明必须以特定顺序运行任务.因此,即使忽略依赖性信息,正确的顺序也是完全有效的选择.

有没有办法编写测试来避免这种"偶然"的成功?在我看来,这是一种相当常见的情况,特别是在采用测试驱动的"不编写没有测试失败的代码"的方法时.

eca*_*mur 1

这个策略在很多时候都有效:

首先,消除任何外部熵源(将线程池设置为使用单个线程;使用预先种子的 PRNG 模拟任何 RNG 等)。然后,重复进行测试以生成每种输出组合,仅更改机器的输入测试中:

from itertools import permutations
def test_add_dependency(self):
    """Tasks can be added with dependencies"""
    for p in permutations("AB"):
        self.done = []
        def test(id):
            self.done.append("Test " + id)
        s = Schedule(threads=1)
        tasks = {id: Task("Test " + id, partial(test, id)) for id in "AB"}
        s.add_task(tasks['A'])
        s.add_task(tasks['B'])
        s.add_dependency(tasks[p[0]], tasks[p[1]])
        s.run()
        self.assertEqual(self.done, ["Test " + p[1], "Test " + p[0]])
Run Code Online (Sandbox Code Playgroud)

Schedule如果未能使用来自 的信息,则该测试将失败add_dependency,因为这是测试运行之间唯一不同的熵源(即信息)。