Question

I have a group of test cases that all should have exactly the same test done, along the lines of "Does method x return the name of an existing file?"

I thought that the best way to do it would be a base class deriving from TestCase that they all share, and simply add the test to that class. Unfortunately, the testing framework still tries to run the test for the base class, where it doesn't make sense.

class SharedTest(TestCase):
    def x(self):
        ...do test...

class OneTestCase(SharedTest):
    ...my tests are performed, and 'SharedTest.x()'...

I tried to hack in a check to simply skip the test if it's called on an object of the base class rather than a derived class like this:

    class SharedTest(TestCase):
        def x(self):
            if type(self) != type(SharedTest()):
                ...do test...
            else:
                pass

but got this error:

ValueError: no such test method in <class 'tests.SharedTest'>: runTest

First, I'd like any elegant suggestions for doing this. Second, though I don't really want to use the type() hack, I would like to understand why it's not working.

Was it helpful?

Solution

I faced a similar problem. I couldn't prevent the test method in the base class being executed but I ensured that it did not exercise any actual code. I did this by checking for an attribute and returning immediately if it was set. This attribute was only set for the Base class and hence the tests ran everywhere else but the base class.

class SharedTest(TestCase):
    def setUp(self):
        self.do_not_run = True

    def test_foo(self):
        if getattr(self, 'do_not_run', False):
            return
        # Rest of the test body.

class OneTestCase(SharedTest):
    def setUp(self):
        super(OneTestCase, self).setUp()
        self.do_not_run = False

This is a bit of a hack. There is probably a better way to do this but I am not sure how.

Update

As sdolan says a mixin is the right way. Why didn't I see that before?

Update 2

(After reading comments) It would be nice if (1) the superclass method could avoid the hackish if getattr(self, 'do_not_run', False): check; (2) if the number of tests were counted accurately.

There is a possible way to do this. Django picks up and executes all test classes in tests, be it tests.py or a package with that name. If the test superclass is declared outside the tests module then this won't happen. It can still be inherited by test classes. For instance SharedTest can be located in app.utils and then used by the test cases. This would be a cleaner version of the above solution.

# module app.utils.test
class SharedTest(TestCase):
    def test_foo(self):
        # Rest of the test body.

# module app.tests
from app.utils import test
class OneTestCase(test.SharedTest):
    ...

OTHER TIPS

You could use a mixin by taking advantage that the test runner only runs tests inheriting from unittest.TestCase (which Django's TestCase inherits from.) For example:

class SharedTestMixin(object):
    # This class will not be executed by the test runner (it inherits from object, not unittest.TestCase.
    # If it did, assertEquals would fail , as it is not a method that exists in `object`
    def test_common(self):
         self.assertEquals(1, 1)


class TestOne(TestCase, SharedTestMixin):
    def test_something(self):
         pass

    # test_common is also run

class TestTwo(TestCase, SharedTestMixin):
    def test_another_thing(self):
        pass

    # test_common is also run

For more information on why this works do a search for python method resolution order and multiple inheritance.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top