8

私は、反復可能なものを返す 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), ...])

そして、これはうまくいきます。しかし、イテラブルをネストした場合はどうなるでしょうか。それはイテラブルを生成するイテラブルですか?式を言うように 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 テスト コミュニティには、イテラブルをテストする際の問題に対する解決策がありますか? 当然のことながら、無限ジェネレーターが必要な場合のように、一部のイテラブルはこの方法でテストできませんが、それでもこの問題は、誰かがこれについて考えたことがあるほど一般的です。

4

4 に答える 4

4

KennyTM の 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)
于 2012-09-28T17:13:51.900 に答える
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)
于 2012-10-04T20:41:45.087 に答える
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

言及すべきことの1つは、オブジェクトtypeを区別するのが粗いことです。<type 'classobj'>

于 2012-09-28T17:02:59.350 に答える
0

Pythonプログラマーがイテラブルをテストする標準的な方法は知りませんが、ネストされたレベルの任意のレベルで機能する再帰関数のアイデア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)

ただし、ご覧のとおり、これには欠陥があります。何かをアンロールすると、常に配列に変換されます。これはイテラブルにとって望ましいことでしたが、タプルにも適用されます。そのため、予想される結果のタプルをリストに手動で変換する必要がありました。したがって、出力がリストまたはタプルである場合は区別できません。

この素朴なアプローチのもう 1 つの問題は、テストに合格しても関数が機能することを意味しないことです。assertEqual(list(my_fun()), [1, 2, 3])「リスト」が に等しい iterable を返す可能性があると思いますが、をチェックするとします[1, 2, 3]。必要に応じて iterable が返されなかった可能性があります。リストまたはタプルも返された可能性があります。

于 2012-09-28T16:21:54.860 に答える