我在测试python函数方面遇到了困难
返回一个可迭代的函数
屈服或仅返回可迭代的函数,如return imap(f, some_iter)
或return permutations([1,2,3])
。
因此,使用排列示例,我希望函数的输出为[(1, 2, 3), (1, 3, 2), ...]
。所以,我开始测试我的代码。
def perm3():
return permutations([1,2,3])
# Lets ignore test framework and such details
def test_perm3():
assertEqual(perm3(), [(1, 2, 3), (1, 3, 2), ...])
这不起作用,因为perm3()
是可迭代的,而不是
名单。所以我们可以修复这个特定的例子。
def test_perm3():
assertEqual(list(perm3()), [(1, 2, 3), (1, 3, 2), ...])
这很好用。但是,如果我有嵌套的iterables怎么办?那是
迭代产生迭代?喜欢说出表达方式
product(permutations([1, 2]), permutations([3, 4]))
。现在这是
可能没有用,但很明显它会(一旦展开了
迭代器)像[((1, 2), (3, 4)), ((1, 2), (4, 3)), ...]
之类的东西。
但是,我们不能只将list
包裹在我们的结果周围,因为这只会
将iterable<blah>
转为[iterable<blah>, iterable<blah>, ...]
。好
当然我可以做map(list, product(...))
,但这只适用于
嵌套水平为2。
那么,python测试社区是否有任何解决方案 测试iterables时出现的问题?当然,一些迭代不能 以这种方式进行测试,就像你想要一个无限的发电机一样,但是 仍然这个问题应该足够让人有所思考 关于这个。
答案 0 :(得分:4)
我使用KennyTM's assertRecursiveEq:
import unittest
import collections
import itertools
class TestCase(unittest.TestCase):
def assertRecursiveEq(self, first, second, *args, **kwargs):
"""
https://stackoverflow.com/a/3124155/190597 (KennyTM)
"""
if (isinstance(first, collections.Iterable)
and isinstance(second, collections.Iterable)):
for first_, second_ in itertools.izip_longest(
first, second, fillvalue = object()):
self.assertRecursiveEq(first_, second_, *args, **kwargs)
else:
# If first = np.nan and second = np.nan, I want them to
# compare equal. np.isnan raises TypeErrors on some inputs,
# so I use `first != first` as a proxy. I avoid dependency on numpy
# as a bonus.
if not (first != first and second != second):
self.assertAlmostEqual(first, second, *args, **kwargs)
def perm3():
return itertools.permutations([1,2,3])
class Test(TestCase):
def test_perm3(self):
self.assertRecursiveEq(perm3(),
[(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)])
if __name__ == '__main__':
import sys
sys.argv.insert(1, '--verbose')
unittest.main(argv = sys.argv)
答案 1 :(得分:2)
<强> 1。如果结果顺序无关紧要
使用unittest.assertItemsEqual()。这测试项目存在于self和reference中,但忽略了顺序。这适用于您的示例一个嵌套的深层示例。它也适用于我炮制的2个深刻的例子。
<强> 2。如果结果的顺序很重要
我建议不要将 perm3()的结果投射到列表中。相反,在迭代时直接比较元素。这是一个适用于您的示例的测试功能。我将它添加到unittest.TestCase的子类:
def assertEqualIterables(self, itable1, itable2):
for ival1, ival2 in zip(itable1, itable2):
if "__iter__" in dir(ival1):
self.assertEqualIterables(ival1, ival2)
else:
self.assertEquals(ival1, ival2)
使用它像:
def test_perm3(self):
reference = [((1, 2), (3, 4)), ((1, 2), (4, 3)),
((2, 1), (3, 4)), ((2, 1), (4, 3)),]
self.assertEqualIterables(perm3(), reference)
答案 2 :(得分:1)
你可以扩展你的建议,包括type
(允许你区分列表,元组等),如下所示:
def unroll(item):
if "__iter__" in dir(item):
return map(unroll, item), type(item)
else:
return item, type(item)
例如:
got = unroll(permutations([1,2]))
([([(1, <type 'int'>), (2, <type 'int'>)], <type 'tuple'>), ([(2, <type 'int'>), (1, <type 'int'>)], <type 'tuple'>)], <type 'itertools.permutations'>)
# note the final: <type 'itertools.permutations'>
expected = [(1, 2), (2, 1)]
assertEqual(x[0], unroll(expected) ) # check underlying
assertEqual(x[1], type(permutations([]) ) # check type
有一点需要注意,type
在区分对象方面很粗略,例如<type 'classobj'>
...
答案 3 :(得分:0)
我不知道python程序员测试iterables的任何标准方式,但是你
可以简单地将您的map
和list
的想法应用到递归函数中
适用于任何级别的嵌套。
def unroll(item):
if "__iter__" in dir(item):
return map(unroll, item)
else:
return item
然后你的测试确实会有效。
def test_product_perms():
got = unroll(product(...))
expected = [[[1, 2], [3, 4]], [[1, 2], [4, 3]], ...]
assertEqual(got, expected)
然而,你可以看到这有一个缺陷。它展开的时候 将永远变成一个数组,这是迭代的愿望,但它 也适用于元组。因此,我必须手动将预期结果中的元组转换为列表。因此,如果输出是列表或元组,则无法区分。
这种天真的方法的另一个问题是通过测试并不意味着
该功能有效。假设您检查assertEqual(list(my_fun()), [1, 2,
3])
,而您认为它可能会返回“列出”时的迭代
等于[1, 2, 3]
。可能是它没有像你一样返回一个iterable
想要,它可能也会返回一个列表或一个元组!