Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write unit tests for python tornado application?

I want to try to write some code following TDD practice. I want to create simple app based on python's tornado framework. I was looking through the internet how people write tests for tornado and found something like this:

class TestSomeHandler(AsyncHTTPTestCase):
    def test_success(self):
        response = self.fetch('/something')
        self.assertEqual(response.code, 200)

Correct me if I'm wrong but it looks more like integration tests. Instead of it I'm trying to write simple unit test for some dummy handler. For example such one:

class SomeHandler(BaseHandler):
    @gen.coroutine
    def get(self):
        try:
            from_date = self.get_query_argument("from", default=None)
            datetime.datetime.strptime(from_date, '%Y-%m-%d')
        except ValueError:
            raise ValueError("Incorrect argument value for from_date = %s, should be YYYY-MM-DD" % from_date)

And test would look like:

class TestSomeHandler(AsyncHTTPTestCase):

    def test_no_from_date_param(self):
        handler = SomeHandler()
        with self.assertRaises(ValueError):
            handler.get()

I know I miss at get() application and request. Not handled yet how to create them.

But my question is, do people write tests for tornado like in first example or somebody calls handlers inside of app? What pattern to follow? It would be nice if somebody have relevant code to share.

like image 998
Viacheslav Kondratiuk Avatar asked Feb 22 '16 17:02

Viacheslav Kondratiuk


People also ask

How do I run a unit test case in Python?

The command to run the tests is python -m unittest filename.py . In our case, the command to run the tests is python -m unittest test_utils.py .


2 Answers

The pattern with AsyncHTTPTestCase is used mostly because, it makes all the request stuff for you. Of course one can also use AsyncTestCase and handle it manually.

AsyncTestCase example. Since it will be testing get method that is coroutine, we will use gen_test to make it a bit simpler. RequestHandler requires Application and HTTPRequest objects. Because we are not relaying of app's settings, ui_methods, etc. Application is a simple mock.

from tornado.testing import AsyncTestCase, gen_test
from tornado.web import Application
from tornado.httpserver import HTTPRequest
from unittest.mock import Mock

class TestSomeHandler(AsyncTestCase):

    @gen_test
    def test_no_from_date_param(self):
        mock_application = Mock(spec=Application)
        payload_request = HTTPRequest(
            method='GET', uri='/test', headers=None, body=None
        )
        handler = SomeHandler(mock_application, payload_request)
        with self.assertRaises(ValueError):
            yield handler.get()

IMHO it is up to you which pattern to use. I choose AsyncHTTPTestCase for http-verb methods (get, post and so on), because:

  • it is easier
  • consumer of this method is HTTP client, so asserts response code, body make a lot of sense
  • it prevents of over-complicated methods

Of course rest of handler's methods tested with AsyncTestCase.

like image 93
kwarunek Avatar answered Sep 28 '22 10:09

kwarunek


AsyncHTTPTestCase is designed for tests like your first example, using self.fetch instead of instantiating Handlers directly.

RequestHandler is not designed to be instantiated by hand without the use of an Application, so if you have pieces of functionality that you would prefer to test without the full HTTP stack, this code should generally be in static functions or non-RequestHandler classes.

like image 34
Ben Darnell Avatar answered Sep 28 '22 11:09

Ben Darnell