Search code examples
python-2.7tornadopython-unittestrethinkdb-python

tearDown not called for unittesting with Tornado


I'm using Rethinkdb and Tornado with rethinkdb.set_loop_type("tornado")

I'm using python unittests to test my server routes.

Here is my unittest base class:

class ServerTest(AsyncHTTPTestCase):
    def setUp(self):
        super(ServerTest, self).setUp()

    def get_app(self):
        return Application(self.routes, debug = False)

    def post(self, route, data):
        result = self.fetch("/%s" % route, method = "POST",
                                body = json.dumps(data)).body
        return json.loads(result)

    def tearDown(self):
        super(ServerTest, self).tearDown()
        conn = yield r.connect()
        yield r.db("test").table("test_table").delete().run(conn)
        conn.close()

I noticed that setUp is running properly, but tearDown is not. All my unittests are passing properly, but print statements in tearDown are not called.

EDIT: I've narrowed it down to the fact that I'm calling yield in tearDown.

EDIT: Adding @gen.coroutine to tearDown shows the print statements, but doesn't perform the delete on the database


Solution

  • Using yield and @gen.coroutine makes a function asynchronous, which changes its interface: the caller must be aware of this change. The unittest framework does not know anything about coroutines, so no method called by unittest may be a coroutine.

    Instead of @gen.coroutine, you can use @tornado.testing.gen_test, which lets you use yield in tests, and in methods called from setUp and tearDown, but not setUp and tearDown themselves (because the generator machinery cannot work before super().setUp() or after super().tearDown(). Use a helper method with gen_test and call it without yield in tearDown:

    def tearDown(self):
        self.tearDownHelper()
        super(ServerTest, self).tearDown()
    
    @tornado.testing.gen_test
    def tearDownHelper(self):
        conn = yield r.connect()
        yield r.db("test").table("test_table").delete().run(conn)
        conn.close()