Testing functions returned iterable in python

I'm having difficulty testing python functions that return iteration, like functions that are inferior or functions that just return iterable, like return imap(f, some_iter) or return permutations([1,2,3]) .

So, with an example of permutations, I expect the output of the function to be [(1, 2, 3), (1, 3, 2), ...] . So, I'm starting to test my code.

 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), ...]) 

This will not work, since perm3() is iterable, not a list. Therefore, we can correct this specific example.

 def test_perm3(): assertEqual(list(perm3()), [(1, 2, 3), (1, 3, 2), ...]) 

And it works great. But what if I have nested iterations? That is, iterations giving iterations? As the expressions say product(permutations([1, 2]), permutations([3, 4])) . Now this is probably not useful, but it’s clear that it will be (when to deploy the iterators) something like [((1, 2), (3, 4)), ((1, 2), (4, 3)), ...] . However, we cannot just wrap the list around our result, as this will turn the iterable<blah> into [iterable<blah>, iterable<blah>, ...] . Well of course, I can do map(list, product(...)) , but this only works for nesting level 2.

So, does the python testing community have any solution for the problem when testing iterations? Naturally, some iterations cannot be tested in such a way as if you want an infinite generator, but nevertheless this question should be widespread enough for someone to think about it.

+8
python iterator testing
source share
4 answers

I am using KennyTM assertRecursiveEq :

 import unittest import collections import itertools class TestCase(unittest.TestCase): def assertRecursiveEq(self, first, second, *args, **kwargs): """ /questions/677233/test-assertions-for-tuples-with-floats/2631457#2631457 (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) 
+4
source share

1. If the order of the results does not matter

Use unittest.assertItemsEqual () . This checks that the elements are present in both self and links, but ignore the order. This works on your example with one nested deep example. It also works on the two-depth example that I came up with.

2. If the order of the results matters

I would advise never listing the perm3 () results in a list. Instead, compare items directly when you repeat. Here is a test function that will work for your example. I added it to a subclass of 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) 

Use it as:

 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
source share

You can expand the sentence by including type (this allowed you to distinguish between lists, tuples, etc.), for example:

 def unroll(item): if "__iter__" in dir(item): return map(unroll, item), type(item) else: return item, type(item) 

For example:

 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 

.

It should be noted that type is rude when distinguishing between objects, for example. <type 'classobj'> ...

+1
source share

I don’t know of any standard way of programming python test iterables programmers, but you can just apply your idea of map and list to a recursive function working at any level of nesting.

 def unroll(item): if "__iter__" in dir(item): return map(unroll, item) else: return item 

Then your test will work.

 def test_product_perms(): got = unroll(product(...)) expected = [[[1, 2], [3, 4]], [[1, 2], [4, 3]], ...] assertEqual(got, expected) 

However, there is a flaw in this, as you can see. When something expands, it will always face the array, this would be desirable for repetitions, but this also applies to tuples. So I had to manually convert the tuples in the expected result into lists. Therefore, you cannot distinguish if the outputs are lists or tuples.

Another problem with this naive approach is that passing a test does not mean that the function works. Say that you are checking assertEqual(list(my_fun()), [1, 2, 3]) , while you think it can return iterability when the "listed" is equal to [1, 2, 3] . Perhaps he did not return the iteration as you wanted, he could return a list or a tuple too!

0
source share

All Articles