Package: infi.unittest
Introductioninfi.unittestis a set of extensions for the standardunittestmodule provided by the Python standard library. It provides several improvements over the original implementation, described below.Noteinfi.unittest does not support Python 2.6 and below, due to the major overhaul in theunittestmodule that took place in 2.7. Sorry.InstallationThe installation is done in the ordinary way:python setup.py installFeaturesTest ParametersOne of the most annoying things aboutunittestis that you cannot easily specify test parameters. If you have a test to test an API with parameters, and you want several cases, each testing a specific value which should work and has a meaning, this can be annoying, and breaks the DRY principle:>>> import unittest
>>> class MyTest(unittest.TestCase):
...     def test__api_call_with_yellow(self):
...         self._test_api_call('yellow')
...     def test__api_call_with_orange(self):
...         self._test_api_call('orange')
...     def _test_api_call(self, color):
...         some_api(color) # test here... yuck!When using infi.unittest, you get a slightly better way of doing this:>>> from infi.unittest import TestCase
>>> from infi.unittest import parameters
>>> class MyTest(TestCase):
...     @parameters.iterate('color', ['orange', 'yellow'])
...     def test__api_call(self, color):
...         some_api(color) # yay!The above will construct one test case per possible value, thus separating the cases for each value. It is also possible to use multiple values, multiplying the psosibilities:>>> class MyTest(TestCase):
...     @parameters.iterate('a', [1, 2, 3])
...     @parameters.iterate('b', [4, 5, 6])
...     def test__some_api(self, a, b):
...         passIteration can be done over functions (or any callable object) that returns the sequence to iterate over:>>> def get_options():
...     return [1, 2, 3]
>>> class MyTest(TestCase):
...     @parameters.iterate('a', get_options)
...     def test__something(self, a):
...         passFor boolean flags, there exists a simpler way to iterate between options:>>> class MyTest(TestCase):
...     @parameters.toggle('a', 'b', 'c')
...     def test__something(self, a, b, c):
...         pass # will be called with all combinations of True/False for a, b, cFixture ParametersSometimes you want to write a set of tests, where thefixturefor running them needs to iterate over options. For instance, if we want to test a utility method over both old-style and new-style classes:>>> class OldStyle:
...     pass
>>> class NewStyle(object):
...     pass
>>> class TestOldStyle(TestCase):
...     def setUp(self):
...         super(TestOldStyle, self).setUp()
...         self.tested_obj = OldStyle()
...     def test__1(self):
...         # do something with self.tested_obj
...         pass
...     def test__2(self):
...         # do something with self.tested_obj
...         pass
>>> class TestNewStyle(TestCase):
...     def setUp(self):
...         super(TestNewStyle, self).setUp()
...         self.tested_obj = NewStyle()
...     def test__1(self):
...         # do something with self.tested_obj
...         pass
...     def test__2(self):
...         # do something with self.tested_obj
...         passA somewher clever, although not very pretty, way of doing this is inheritance:>>> class _BaseTest(TestCase):
...     def test__1(self):
...         # do something with self.tested_obj
...         pass
...     def test__2(self):
...         # do something with self.tested_obj
...         pass
>>> class OldStyleTest(_BaseTest):
...     def setUp(self):
...         super(OldStyleTest, self).setUp()
...         self.tested_obj = OldStyle()
>>> class NewStyleTest(_BaseTest):
...     def setUp(self):
...         super(NewStyleTest, self).setUp()
...         self.tested_obj = NewStyle()This is yucky, and some discovery methods will attempt to run _BaseTest as well (although prefixed with an underscore).infi.unittestsolves this elegantly:>>> class Test(TestCase):
...     @parameters.iterate('obj', [NewStyle(), OldStyle()])
...     def setUp(self, obj):
...         super(Test, self).setUp()
...         self.tested_obj = obj
...     def test__1(self):
...         # do something with self.tested_obj
...         pass
...     def test__2(self):
...         # do something with self.tested_obj
...         passinfi.unittest can even multiply across inheritence. This means that the following code will eventually test the cartesian product between [1, 2, 3] and [4, 5, 6]>>> class BaseTest(TestCase):
...     @parameters.iterate('param', [1, 2, 3])
...     def setUp(self, param):
...         super(BaseTest, self).setUp()
...         self.base_param = param
>>> class DerivedTest(BaseTest):
...     @parameters.iterate('param', [4, 5, 6])
...     def setUp(self, param):
...         super(DerivedTest, self).param()
...         self.derived_param = param
...     def test(self):
...         self.do_something_with(self.base_param, self.derived_param)Note that even the super() call to setUp doesn’t need to bother with the parameter(s) - it gets automatically bound.Abstract Base TestsSometimes you would like to include a ‘base test’ to facilitate code reuse. For example in cases like:>>> class FileTestBase(TestCase):
...     def test__has_write_method(self):
...         self.assertTrue(hasattr(self.file, "write"))
...     def test__has_read_method(self):
...         self.assertTrue(hasattr(self.file, "read"))
>>> class RegularFileTest(FileTestBase):
...     def setUp(self):
...         super(RegularFileTest, self).setUp()
...         self.file = open("somefile", "wb")
>>> class SocketFileTest(FileTestBase):
...     def setUp(self):
...         super(SocketFileTest, self).setUp()
...         self.file = connect_to_some_server().makefile()In this case a regular test discovery mechanism would catch flames, because it would attempt to run FileTestBase itself, which has an incomplete setUp scheme.infi.unittestprovides an easy shortcut for this. Just mark the base class like so:>>> from infi.unittest import abstract_base_test
>>> @abstract_base_test
... class FileTestBase(TestCase):
...     passAnd the base test itself will not be run directly.Notenoseas an issue with this technique. There is a workaround in place, but it involves live patching of a nose utility function, so there is a chance it will break in updates, or not work at all in some cases. Until this issue is resolved (https://github.com/nose-devs/nose/issues/502), nose support should be considered experimental.Nose Integrationinfi.unittestbreaks compatibility with the excellentnose: <http://code.google.com/p/python-nose/>tool, so it provides a nose plugin to handle with its tests. The plugin is automatically enabled for you, so you don’t have to pass the –with-infi flag to nosetests.