Python parameterized unittest by subclassing TestCase
Question: How can I create multiple TestCases and run them programmatically? I'm trying
to test multiple implementations of a collection on a common TestCase.

I'd prefer to stick to with plain unittest and avoid dependencies.

Here's some resources that I looked at that didn't quite meet what I wanted:

  * [Writing a re-usable parametrized unittest.TestCase method](http://stackoverflow.com/questions/1676269/) \- The accepted answer proposes four different external libraries.

  * <http://eli.thegreenplace.net/2011/08/02/python-unit-testing-parametrized-test-cases> \- This approach uses a static method `paramerize`. I don't understand why you can't pass in a parameter directly into the `TestSubClass.__init__`.

  * [How to generate dynamic (parametrized) unit tests in python?](http://stackoverflow.com/questions/32899/) \- A little bit too black magic.

Here's a minimal (non)working example.

    
    
    import unittest
    
    MyCollection = set
    AnotherCollection = set
    # ... many more collections
    
    
    def maximise(collection, array):
        return 2
    
    
    class TestSubClass(unittest.TestCase):
    
        def __init__(self, collection_class):
            unittest.TestCase.__init__(self)
            self.collection_class = collection_class
            self.maximise_fn = lambda array: maximise(collection_class, array)
    
    
        def test_single(self):
            self.assertEqual(self.maximise_fn([1]), 1)
    
    
        def test_overflow(self):
            self.assertEqual(self.maximise_fn([3]), 1)
    
        # ... many more tests
    
    
    def run_suite():
        suite = unittest.defaultTestLoader
        for collection in [MyCollection, AnotherCollection]:
            suite.loadTestsFromTestCase(TestSubClass(collection))
        unittest.TextTestRunner().run(suite)
    
    
    def main():
        run_suite()
    
    
    if __name__ == '__main__':
        main()
    

The above approach errors with in `loadTestsFromTestCase`:

`TypeError: issubclass() arg 1 must be a class`


Answer: How about using [`pytest` with to parametrize
fixture](https://pytest.org/latest/fixture.html#fixture-parametrize):

    
    
    import pytest
    
    MyCollection = set
    AnotherCollection = set
    
    
    def maximise(collection, array):
        return 1
    
    @pytest.fixture(scope='module', params=[MyCollection, AnotherCollection])
    def maximise_fn(request):
        return lambda array: maximise(request.param, array)
    
    def test_single(maximise_fn):
        assert maximise_fn([1]) == 1
    
    def test_overflow(maximise_fn):
        assert maximise_fn([3]) == 1
    

If that's not an option, you can make a mixin to contain test function, and
subclasses to provide `maximise_fn`s:

    
    
    import unittest
    
    MyCollection = set
    AnotherCollection = set
    
    
    def maximise(collection, array):
        return 1
    
    
    class TestCollectionMixin:
        def test_single(self):
            self.assertEqual(self.maximise_fn([1]), 1)
    
        def test_overflow(self):
            self.assertEqual(self.maximise_fn([3]), 1)
    
    
    class TestMyCollection(TestCollectionMixin, unittest.TestCase):
        maximise_fn = lambda self, array: maximise(MyCollection, array)
    
    
    class TestAnotherCollection(TestCollectionMixin, unittest.TestCase):
        maximise_fn = lambda self, array: maximise(AnotherCollection, array)
    
    
    if __name__ == '__main__':
        unittest.main()
    

