Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I add a test method to a group of Django TestCase-derived classes?

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.

like image 875
JivanAmara Avatar asked Aug 31 '10 03:08

JivanAmara


People also ask

How do you write test cases in Django?

To write a test you derive from any of the Django (or unittest) test base classes (SimpleTestCase, TransactionTestCase, TestCase, LiveServerTestCase) and then write separate methods to check that specific functionality works as expected (tests use "assert" methods to test that expressions result in True or False values ...

What is automated testing in Django?

Automated Testing is a widely used tool that uses a collection of predefined test cases. Learn how Django and Selenium to ensure a bug free system. Table of contents.

Is testing important in Django?

Testing is vital. Without properly testing your code, you will never know if the code works as it should, now or in the future when the codebase changes. Countless hours can be lost fixing problems caused by changes to the codebase.


2 Answers

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.

like image 189
Sam Dolan Avatar answered Sep 20 '22 14:09

Sam Dolan


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):
    ...
like image 30
Manoj Govindan Avatar answered Sep 20 '22 14:09

Manoj Govindan