tornado.testing.AsyncHTTPTestCase: Avoid make Application every times

Created on 20 Jun 2017  路  3Comments  路  Source: tornadoweb/tornado

I have a lot tests that inherited tornado.testing.AsyncHTTPTestCase, recently I find the testing is very slow and I find the AsyncHTTPTestCase make Application for every tests.

PS. I've override the Application and put some heavy jobs.

Most helpful comment

An important feature of AsyncHTTPTestCase (and the AsyncTestCase from which it inherits) is that the test case destroys and recreates the IOLoop for each test. This ensures that each test begins in a clean state unaffected by the previous one. The Application instance can't be reused between tests because the Application stores a reference to the current IOLoop.

I would try two methods to speed up your tests. First, cache the result of the heavy jobs required to start up your Application. Use a new Application for each test, but speed up Application initialization by using these cached results.

If you can't do that, you could try overriding AsyncHTTPTestCase's get_new_ioloop method to return IOLoop.current(), and move the initialization of your Application into setUpClass so it's done once per test class instead of once per test:

from tornado.ioloop import IOLoop
from tornado.testing import AsyncHTTPTestCase
from tornado.web import Application, RequestHandler


class HelloHandler(RequestHandler):
    def get(self):
        self.write("Hello world!")


class MyTestCase(AsyncHTTPTestCase):
    @classmethod
    def setUpClass(cls):
        cls.my_app = Application([(r'/', HelloHandler)])

    def get_new_ioloop(self):
        return IOLoop.current()

    def get_app(self):
        return self.my_app

    def test(self):
        response = self.fetch("/")
        self.assertEqual(response.body, b"Hello world!")

if __name__ == '__main__':
    import unittest
    unittest.main()

All 3 comments

An important feature of AsyncHTTPTestCase (and the AsyncTestCase from which it inherits) is that the test case destroys and recreates the IOLoop for each test. This ensures that each test begins in a clean state unaffected by the previous one. The Application instance can't be reused between tests because the Application stores a reference to the current IOLoop.

I would try two methods to speed up your tests. First, cache the result of the heavy jobs required to start up your Application. Use a new Application for each test, but speed up Application initialization by using these cached results.

If you can't do that, you could try overriding AsyncHTTPTestCase's get_new_ioloop method to return IOLoop.current(), and move the initialization of your Application into setUpClass so it's done once per test class instead of once per test:

from tornado.ioloop import IOLoop
from tornado.testing import AsyncHTTPTestCase
from tornado.web import Application, RequestHandler


class HelloHandler(RequestHandler):
    def get(self):
        self.write("Hello world!")


class MyTestCase(AsyncHTTPTestCase):
    @classmethod
    def setUpClass(cls):
        cls.my_app = Application([(r'/', HelloHandler)])

    def get_new_ioloop(self):
        return IOLoop.current()

    def get_app(self):
        return self.my_app

    def test(self):
        response = self.fetch("/")
        self.assertEqual(response.body, b"Hello world!")

if __name__ == '__main__':
    import unittest
    unittest.main()

Thanks for your reply, I've cached the Application instance via a class property. I open this to want know if that could be avoid in framework level.

Great, I'm glad you've found a solution to your scenario. We're not going to change how AsyncHTTPTestCase works, because destroying and recreating the IOLoop and the Application is usually fast, and it's an important way to isolate tests from each other.

Was this page helpful?
0 / 5 - 0 ratings