Skip to main content

Django test utility for validating Swagger documentation

Project description

Django Swagger Tester

https://img.shields.io/pypi/v/django-swagger-tester.svg https://img.shields.io/pypi/pyversions/django-swagger-tester.svg https://img.shields.io/pypi/djversions/django-swagger-tester.svg Documentation Status https://codecov.io/gh/sondrelg/django-swagger-tester/branch/master/graph/badge.svg

https://img.shields.io/badge/code%20style-black-000000.svg Checked with mypy https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white

Documentation: https://django-swagger-tester.readthedocs.io

Repository: https://github.com/sondrelg/django-swagger-tester


This package is a simple test utility for your Django Swagger documentation.

Its aim is to make it easy for developers to catch and correct documentation errors in their Swagger docs by comparing documented responses to actual API responses, or validating documented request bodies using actual input serializers.

Features

The package has three main features:

Implementations

This package currently supports:

  • Testing of dynamically rendered OpenAPI schemas using using drf_yasg

  • Testing of any implementation which generates a static schema yaml or json file (e.g., like DRF)

If you’re using another method to generate your documentation and would like to use this package, feel free to add an issue, or create a PR. Adding a new implementation is as easy as adding the required logic needed to load the OpenAPI schema.

Installation

Install using pip:

pip install django-swagger-tester

Configuration

Settings

To use Django Swagger Settings in your project, you first need to add a django_swagger_tester to your installed apps.

INSTALLED_APPS = [
    ...
    'django_swagger_tester',
]

Secondly, you need to configure the SWAGGER_TESTER package settings in your settings.py:

from django_swagger_tester.loaders import StaticSchemaLoader
from django_swagger_tester.case_testers import is_camel_case

SWAGGER_TESTER = {
    'SCHEMA_LOADER': StaticSchemaLoader,
    'PATH': './static/openapi-schema.yml',
    'CASE_TESTER': is_camel_case,
    'CASE_PASSLIST': [],
    'CAMEL_CASE_PARSER': False,
}

Parameters

SCHEMA_LOADER

The loader class you use is dictated by how your OpenAPI schema is generated. If your schema is a static file, you should use the StaticSchemaLoader. If not, you should select the loader class that serves your implementation.

Loader classes can be imported from django_swagger_tester.loaders and currently include:

  • StaticSchemaLoader

  • DrfYasgSchemaLoader

Example:

from django_swagger_tester.loaders import DrfYasgSchemaLoader

SWAGGER_TESTER = {
    'SCHEMA_LOADER': DrfYasgSchemaLoader,
}

PATH

Path takes the file path of your OpenAPI schema file. this is only required if you’re using the StaticSchemaLoader loader class.

Example:

SWAGGER_TESTER = {
    'PATH': BASE_DIR / '/openapi-schema.yml',
}

CASE_TESTER

The callable passed for this input decide the naming standard you wish to enforce for your documentation.

There are currently four supported options:

  • camel case

  • snake case

  • pascal case

  • kebab case

  • or you can pass None to skip case validation completely

Your OpenAPI schema will be tested to make sure all parameter names are correctly cased according to this preference. If you do not wish to enforce this check, you can specify None to skip this feature.

Example:

from django_swagger_tester.case_testers import is_camel_case

SWAGGER_TESTER = {
    'CASE_TESTER': is_camel_case,
}

Default: None

CASE_PASSLIST

List of string for ignoring exceptions from general case-testing. Say you’ve decided that all your responses should be camel cased, but you’ve already made IP a capitalized response key; you can the add the key to your CASE_PASSLIST to avoid this being flagged as an error in your tests.

Example:

from django_swagger_tester.case_testers import is_camel_case

SWAGGER_TESTER = {
    'CASE_PASSLIST': ['IP', 'DHCP'],
}

Default: []

CAMEL_CASE_PARSER

Should be set to True if you use djangorestframework-camel-case’s CamelCaseJSONParser or CamelCaseJSONRenderer for your API views.

Example:

SWAGGER_TESTER = {
    'CAMEL_CASE_PARSER': True,
}

Default: False

Response Validation

To make sure your API response matches your documented response, the validate_response function compares the two at each level of depth.

A pytest implementation might look like this:

from django_swagger_tester.testing import validate_response

def test_200_response_documentation(client):
    route = 'api/v1/test/1'
    response = client.get(route)
    assert response.status_code == 200
    assert response.json() == expected_response

    # test swagger documentation
    validate_response(response=response, method='GET', route=route)

A Django-test implementation might look like this:

from django_swagger_tester.testing import validate_response

class MyApiTest(APITestCase):

    path = '/api/v1/test/'

    def setUp(self) -> None:
        user, _ = User.objects.update_or_create(username='test_user')
        self.client.force_authenticate(user=user)

    def test_get_200(self) -> None:
        response = self.client.get(self.path, headers={'Content-Type': 'application/json'})
        expected_response = [...]

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json(), expected_response)

        # test swagger documentation
        validate_response(response=response, method='GET', route=self.path)

It is also possible to test more than a single response at the time:

def test_post_endpoint_responses(client):
    # 201 - Resource created
    response = client.post(...)
    validate_response(response=response, method='POST', route='api/v1/test/')

    # 400 - Bad data
    response = client.post(...)
    validate_response(response=response, method='POST', route='api/v1/test/')

def test_get_endpoint_responses(client):
    # 200 - Fetch resource
    response = client.get(...)
    validate_response(response=response, method='GET', route='api/v1/test/<id>')

    # 404 - Bad ID
    response = client.get(...)
    validate_response(response=response, method='GET', route='api/v1/test/<bad id>')

Errors

When found, errors will be raised in the following format:

django_swagger_tester.exceptions.SwaggerDocumentationError: Item is misspecified:

Summary
-------------------------------------------------------------------------------------------

Error:      The following properties seem to be missing from your response body: length, width.

Expected:   {'name': 'Saab', 'color': 'Yellow', 'height': 'Medium height', 'width': 'Very wide', 'length': '2 meters'}
Received:   {'name': 'Saab', 'color': 'Yellow', 'height': 'Medium height'}

Hint:       Remove the key(s) from you Swagger docs, or include it in your API response.
Sequence:   init.list

-------------------------------------------------------------------------------------------

* If you need more details: set `verbose=True`

Expected describes the response data, and Received describes the schema. In this example, the response data is missing two attributes, height and width, documented in the OpenAPI schema indicating that either the response needs to include more data, or that the OpenAPI schema should be corrected.

Some errors will include hints to help you understand what actions to take, to rectify the error.

Finally, all errors will include a Sequence string indicating how the response tester has iterated through the orignal data structure, before finding an error.

Input Validation

To make sure your request body documentation is accurate, and will stay accurate, you can use endpoint serializers to validate your schema directly.

validate_input_serializer constructs an example representation of the documented request body, and passes it to the serializer it is given. This means it’s only useful if you use serializers for validating your incoming request data.

A Django test implementation of input validation for a whole project could be structured like this:

from django.test import SimpleTestCase
from django_swagger_tester.testing import validate_input_serializer

from api.serializers.validation.request_bodies import ...


class TestSwaggerInput(SimpleTestCase):
    endpoints = [
        {
            'api/v1/orders/': [
                ('POST', ValidatePostOrderBody),
                ('PUT', ValidatePutOrderBody),
                ('DELETE', ValidateDeleteOrderBody)
            ]
        },
        {
            'api/v1/orders/<id>/entries/': [
                ('POST', ValidatePostEntryBody),
                ('PUT', ValidatePutEntryBody),
                ('DELETE', ValidateEntryDeleteBody)
            ]
        },
    ]

    def test_swagger_input(self) -> None:
        """
        Verifies that the documented request bodies are valid.
        """
        for endpoint in self.endpoints:
            for route, values in endpoint.items():
                for method, serializer in values:
                    validate_input_serializer(serializer=serializer, method=method, route=route)

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-swagger-tester-2.0.0.tar.gz (23.9 kB view details)

Uploaded Source

Built Distribution

django_swagger_tester-2.0.0-py3-none-any.whl (26.5 kB view details)

Uploaded Python 3

File details

Details for the file django-swagger-tester-2.0.0.tar.gz.

File metadata

  • Download URL: django-swagger-tester-2.0.0.tar.gz
  • Upload date:
  • Size: 23.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.0.10 CPython/3.8.5 Linux/5.4.0-1025-azure

File hashes

Hashes for django-swagger-tester-2.0.0.tar.gz
Algorithm Hash digest
SHA256 3003c580a1a9d3e8c4c2e98a33a788c0609296fb8255b85d5cb9459850127281
MD5 10b32b5885ced1e2c438a3475cd3e082
BLAKE2b-256 0aca0b145e7525a62c5c79a7eb248b682d9223f06bb619c737c6e7e2040ae243

See more details on using hashes here.

File details

Details for the file django_swagger_tester-2.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_swagger_tester-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5acc5e54624da3721c9d299a0acca6f7ac06629ef99fccba7bc7570d344c4b0a
MD5 534761a7f903c0d8237ef82fc5579cbf
BLAKE2b-256 9a510ccfe4d37e1eb4047a266e72379e63a29981be0d519c03975eba28005ae9

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page