Search code examples
pythonunit-testingtestingtornado

Can I have an abstract base class for my tornado.testing.AsyncTestCase tests?


Is it possible to have base classes that define tests in tornado that are themselves not run as tests?

Let's say I have the following minimal example as a base class:

from tornado.testing import AsyncTestCase, gen_test
from tornado.httpclient import HTTPRequest

class AbstractTestCase(AsyncTestCase):
    def set_parameters(self):
        #Set some parameter value here: self.uri = ...
        raise NotImplementedError

    @gen_test
    def test_common_functionality(self):
        req = HTTPRequest(self.uri, method = "GET")
        response = yield self.client.fetch(req, raise_error=False)
        self.assertEqual(200, response.code)

Now, I would like to make several test cases that inherit from this, define their own value for self.uri...and have some specific tests of their own. Like this:

class ConcreteTestCase(AbstractTestCase):
    def set_parameters(self):
        self.uri = "www.stackoverflow.com"

    @gen_test
    def test_some_other_thing(self):
        self.assertEqual(2, 1+1)

However, when I try to run this, the AbstractTestCase is also run on its own, giving an error (the NotImplementedError). This happens even when I only try to run the inheriting tests.

Is there any way around this issue, or do I have to duplicate the functionality in each test case?


Solution

  • One way to do this is with multiple inheritance. The abstract class doesn't need to extend AsyncTestCase as long as that class is in the inheritance hierarchy at runtime.

    class AbstractTestCase(object):
        def set_parameters(self):
            #Set some parameter value here: self.uri = ...
            raise NotImplementedError
    
        @gen_test
        def test_common_functionality(self):
            req = HTTPRequest(self.uri, method = "GET")
            response = yield self.client.fetch(req, raise_error=False)
            self.assertEqual(200, response.code)
    
    class ConcreteTestCase(AbstractTestCase, AsyncTestCase):
        def set_parameters(self):
            self.uri = "www.stackoverflow.com"
    
        @gen_test
        def test_some_other_thing(self):
            self.assertEqual(2, 1+1)
    

    This is admittedly a little weird and mypy type checking doesn't like it. But it's simple and it works, and I haven't found a mypy-friendly alternative that I like.