测试python函数的非确定性行为

时间:2014-02-20 20:21:25

标签: python dictionary iteration non-deterministic

我们有一个庞大而复杂的功能,需要具有确定性。它是我们公司的主力之一,涵盖了大量的代码。由于python的dict迭代器,这段代码经常变得不确定。这已经发生了很多次,并且很难追踪,并且通常不会立即注意到。我们想编写一个自动测试来检测非确定性,但我不知道该怎么做。

我们已经尝试在循环中运行该函数,并且测试结果总是相同的,但有时,即使函数是非确定性的,该函数也会通​​过此测试,因为该函数的任意但有些一致的顺序dict iterator。

有没有办法编写自动化测试来捕获这种错误?

也许有一种方法可以破解python的dict,以便迭代器在这个测试中是随机的而不是随意的?那样重复调用函数会更容易发生分歧吗?这似乎是一个相当复杂的方法,但我想不出任何其他方式。

编辑:

我们目前正在使用Python 2.7。

我们对各个子模块进行了单元测试,但是由于字典顺序的任意但一致的性质,它们通常不会暴露非确定性。

另外,也许非确定性不是描述这个问题的正确方法。这个函数接受{id:data},但是id的值不应该影响代码的结果,但是由于python dict的排序,它有时会这样做。也许测试这个的最好方法是用随机值替换id,并检查多次运行不同id后输出是否相同。

2 个答案:

答案 0 :(得分:5)

如果要随机化散列种子,可以将-R标志指定为python:

-R     : use a pseudo-random salt to make hash() values of various types be
         unpredictable between separate invocations of the interpreter, as
         a defense against denial-of-service attacks

a la

~$ python -c "print {y:x for x,y in enumerate('foobar')}"
{'a': 4, 'r': 5, 'b': 3, 'o': 2, 'f': 0} #it will always be this
~$ python -R -c "print {y:x for x,y in enumerate('foobar')}"
{'a': 4, 'b': 3, 'r': 5, 'f': 0, 'o': 2}
~$ python -R -c "print {y:x for x,y in enumerate('foobar')}"
{'a': 4, 'b': 3, 'r': 5, 'o': 2, 'f': 0}
~$ python -R -c "print {y:x for x,y in enumerate('foobar')}"
{'f': 0, 'o': 2, 'b': 3, 'r': 5, 'a': 4}
~$ python -R -c "print {y:x for x,y in enumerate('foobar')}"
{'r': 5, 'f': 0, 'o': 2, 'a': 4, 'b': 3}

请注意,此行为是python 3.3中的默认行为。

答案 1 :(得分:0)

您可以使用OrderedDict强制两个类似的dict s具有不同的"顺序"。

使用这些作为代码的输入而非vanilla dict,您可以可靠地检查与dict订单问题相关的代码行为。

例如,此测试有时会失败(相对很少):

d1 = {'a':1, 'b': 2}
d2 = dict(d1)

j1 = json.dumps(d1)
j2 = json.dumps(d2)

assert j1 == j2:

并且此测试无法预测:

import json
from collections import OrderedDict

d1 = OrderedDict([('a', 1), ('b', 2)])
d2 = OrderedDict([('b', 2), ('a', 1)])

j1 = json.dumps(d1)
j2 = json.dumps(d2)
assert j1 == j2

但是,这可能更适合单元测试小功能。如果你正在测试一个大而复杂的功能"同时,很可能dict s在函数内生成,因此对输入的作用不够。