在python中测试返回iterable的函数

在python中测试返回iterable的函数,python,testing,iterator,Python,Testing,Iterator,我在测试python函数时遇到了困难 返回一个iterable,与 产生只返回iterable的or函数,如返回imap(f,some_iter)或返回置换([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():

我在测试python函数时遇到了困难 返回一个iterable,与 产生只返回iterable的or函数,如
返回imap(f,some_iter)
返回置换([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()
是一个iterable,而不是一个 列表所以我们可以修正这个特殊的例子

def test_perm3():
  assertEqual(list(perm3()), [(1, 2, 3), (1, 3, 2), ...])
这个很好用。但是如果我有嵌套的iterables呢?就是 伊特拉伯尔屈服于伊特拉伯尔?比如说这些表情
产品(置换([1,2])、置换([3,4])
。现在这是 可能没有用,但很明显,它将(一旦展开 迭代器)类似于
[((1,2),(3,4)),((1,2),(4,3)),…]
。 然而,我们不能仅围绕结果包装
列表
,因为这只会 将
iterable
转到
[iterable,iterable,…]
。嗯 当然我可以做
映射(列表,产品(…)
,但这只适用于 嵌套级别为2

那么,python测试社区有什么解决方案吗 测试iterables时出现的问题?当然,有些人不能 以这种方式进行测试,就像你想要一个无限的生成器,但是 不过,这个问题应该足够普遍,有人会想一想
关于这一点。

我不知道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)
然而,正如您所看到的,这有一个缺陷。展开某物时,它会 将始终转换为数组,这是iterables所希望的,但是 也适用于元组。因此,我必须手动将预期结果中的元组转换为列表。因此,如果输出是列表或元组,则无法进行区分

这种天真方法的另一个问题是,通过测试并不意味着 该函数可以正常工作。假设您选中了
assertEqual(list(my_fun()),[1,2,
3] )
,而您认为它可能会返回一个iterable,当“列出”为 等于
[1,2,3]
。这可能是因为它没有像您那样返回iterable
如果需要,它可能也会返回一个列表或元组

您可以扩展您的建议以包括
类型
(这允许您区分列表、元组等),如下所示:

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
get=unroll(排列([1,2]))
([([(1, ), (2, )], ), ([(2, ), (1, )], )], )
#注意最后一点:
预期=[(1,2)、(2,1)]
assertEqual(x[0],展开(预期))#检查基础
assertEqual(x[1],类型(置换([]))#检查类型

需要指出的一点是,
类型
在区分对象方面比较粗糙,例如,
..

我使用:


1.如果结果的顺序无关紧要

使用。这测试项目是否同时存在于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)

我更喜欢
isinstance()
而不是
type()
@AshwiniChaudhary当我发布/离开工作时,我在想有一个更好的方法:)有没有其他人注意到assertItemsEqual正在处理几个深嵌套的迭代器?我真的没想到它会起作用。。。我疯了吗?我会接受这个,因为它和dbw的答案一样好。与我的答案不同,您可以混合
元组
/
列表
。此答案也是复制粘贴可运行的。:)但是如果有一个答案,您(1)实际检查它是否返回一个iterable(将
返回置换([1,2,3])
更改为
返回列表(置换([1,2,3]))
不应通过,以及(2)嵌套的预期值应具有正确的类型,这就是将其中一个元组更改为列表不应通过(将
(2,3,1)
更改为
[2,3,1]
)。啊。这很公平。我想我通常避免测试类型,而是尝试测试接口。这让实现细节在某些方面发生变化,同时继续生成相同的数据/结果。
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)