Testing API Responses

Tornado OpenAPI 3 includes a base test class to help you validate each of your application’s responses while you test its behavior.

Making your tests aware of your API specification

By extending AsyncOpenAPITestCase, you can define your test cases with your specification attached. Every response returned by fetch() will be automatically checked against your specification to ensure they match the formats documented, and exceptions will be raised when they do not.

Because it extends tornado.testing.AsyncHTTPTestCase, you can write your application tests as you normally would with added confidence that your API is behaving exactly as you expect it to.

import unittest

import tornado.web
from tornado_openapi3.testing import AsyncOpenAPITestCase


class RootHandler(tornado.web.RequestHandler):
    async def get(self):
        self.finish("Hello, World!")


class BaseTestCase(AsyncOpenAPITestCase):
    spec_dict = {
        "openapi": "3.0.0",
        "info": {
            "title": "Simple Example",
            "version": "1.0.0",
        },
        "paths": {
            "/": {
                "get": {
                    "responses": {
                        "200": {
                            "description": "Index",
                            "content": {
                                "text/html": {
                                    "schema": {"type": "string"},
                                }
                            },
                        }
                    }
                }
            }
        },
    }

    def get_app(self):
        return tornado.web.Application([(r"/", RootHandler)])

    def test_root_endpoint(self):
        response = self.fetch("/")
        self.assertEqual(200, response.code)
        self.assertEqual(b"Hello, World!", response.body)


if __name__ == "__main__":
    unittest.main()

Adding custom deserializers

If your endpoints make use of content types beyond application/json, you must add them to this dictionary with a deserializing method that converts the raw body (as bytes or str) to Python objects.

import json

from tornado_openapi3.testing import AsyncOpenAPITestCase


class TestCase(AsyncOpenAPITestCase):
    custom_media_type_deserializers = {
        "application/vnd.example.resource+json": json.loads,
    }

    ...

Adding custom formatters

If your schemas make use of format modifiers, you may specify them in this dictionary paired with a Formatter object that provides methods to validate values and unmarshal them into Python objects.

import datetime

from tornado_openapi3.testing import AsyncOpenAPITestCase


class USDateFormatter:
    def validate(self, value: str) -> bool:
        return bool(re.match(r"^\d{1,2}/\d{1,2}/\d{4}$", value))

    def unmarshal(self, value: str) -> datetime.date:
        return datetime.datetime.strptime(value, "%m/%d/%Y").date()


class TestCase(AsyncOpenAPITestCase):
    custom_formatters = {
        "usdate": USDateFormatter(),
    }

    ...