测试函数在python中返回迭代

时间:2012-09-28 16:21:55

标签: python testing iterator

我在测试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时出现的问题?当然,一些迭代不能 以这种方式进行测试,就像你想要一个无限的发电机一样,但是 仍然这个问题应该足够让人有所思考 关于这个。

4 个答案:

答案 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的任何标准方式,但是你 可以简单地将您的maplist的想法应用到递归函数中 适用于任何级别的嵌套。

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 想要,它可能也会返回一个列表或一个元组!