{"id":13416071,"url":"https://github.com/snok/drf-openapi-tester","last_synced_at":"2025-03-14T23:31:17.389Z","repository":{"id":41094014,"uuid":"232892108","full_name":"snok/drf-openapi-tester","owner":"snok","description":"Test utility for validating OpenAPI documentation","archived":true,"fork":false,"pushed_at":"2023-11-19T17:47:20.000Z","size":4319,"stargazers_count":118,"open_issues_count":8,"forks_count":23,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-10-07T13:06:32.819Z","etag":null,"topics":["api","api-documentation","django","django-rest-framework","django-test","drf","openapi","openapi-schema","openapi3","pytest","swagger","testing"],"latest_commit_sha":null,"homepage":"https://github.com/snok/django-openapi-tester","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/snok.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-01-09T19:49:31.000Z","updated_at":"2024-08-12T19:56:27.000Z","dependencies_parsed_at":"2024-01-11T19:17:10.853Z","dependency_job_id":null,"html_url":"https://github.com/snok/drf-openapi-tester","commit_stats":{"total_commits":911,"total_committers":20,"mean_commits":45.55,"dds":0.5334796926454446,"last_synced_commit":"b2aa34e538f153d3244d41ef5b8c3d5f41c25ff5"},"previous_names":["snok/django-swagger-tester","sondrelg/django-swagger-tester"],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snok%2Fdrf-openapi-tester","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snok%2Fdrf-openapi-tester/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snok%2Fdrf-openapi-tester/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snok%2Fdrf-openapi-tester/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/snok","download_url":"https://codeload.github.com/snok/drf-openapi-tester/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243495493,"owners_count":20299921,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","api-documentation","django","django-rest-framework","django-test","drf","openapi","openapi-schema","openapi3","pytest","swagger","testing"],"created_at":"2024-07-30T21:00:54.049Z","updated_at":"2025-03-14T23:31:16.496Z","avatar_url":"https://github.com/snok.png","language":"Python","funding_links":[],"categories":["Third-Party Packages","Packages"],"sub_categories":["Testing","Documentation"],"readme":"[![PyPI](https://img.shields.io/pypi/v/drf-openapi-tester.svg)](https://pypi.org/project/drf-openapi-tester/)\n[![Coverage](https://codecov.io/gh/snok/drf-openapi-tester/branch/master/graph/badge.svg)](https://codecov.io/gh/snok/drf-openapi-tester)\n[![Python versions](https://img.shields.io/badge/Python-3.7%2B-blue)](https://pypi.org/project/drf-openapi-tester/)\n[![Django versions](https://img.shields.io/badge/Django-3.0%2B-blue)](https://pypi.org/project/drf-openapi-tester/)\n\n\n# DRF OpenAPI Tester\n\nThis is a test utility to validate DRF Test Responses against OpenAPI 2 and 3 schema. It has built-in support for:\n\n- OpenAPI 2/3 yaml or json schema files.\n- OpenAPI 2 schemas created with [drf-yasg](https://github.com/axnsan12/drf-yasg).\n- OpenAPI 3 schemas created with [drf-spectacular](https://github.com/tfranzel/drf-spectacular).\n\n\u003e This project is no longer maintained. If you want to use it, consider taking a look at [this fork](https://github.com/maticardenas/drf-openapi-tester/) which might still be.\n\n## Installation\n\n```shell script\npip install drf-openapi-tester\n```\n\n## Usage\n\nInstantiate one or more instances of `SchemaTester`:\n\n```python\nfrom openapi_tester import SchemaTester\n\nschema_tester = SchemaTester()\n```\n\nIf you are using either [drf-yasg](https://github.com/axnsan12/drf-yasg)\nor [drf-spectacular](https://github.com/tfranzel/drf-spectacular) this will be auto-detected, and the schema will be\nloaded by the `SchemaTester` automatically.\n\nIf you are using schema files, you will need to pass the file path:\n\n```python\nfrom openapi_tester import SchemaTester\n\n# path should be a string\nschema_tester = SchemaTester(schema_file_path=\"./schemas/publishedSpecs.yaml\")\n```\n\nOnce you've instantiated a tester, you can use it to test responses:\n\n```python\nfrom openapi_tester.schema_tester import SchemaTester\n\nschema_tester = SchemaTester()\n\n\ndef test_response_documentation(client):\n    response = client.get('api/v1/test/1')\n    assert response.status_code == 200\n    schema_tester.validate_response(response=response)\n```\n\nIf you are using the Django testing framework, you can create a base `APITestCase` that incorporates schema validation:\n\n```python\nfrom rest_framework.response import Response\nfrom rest_framework.test import APITestCase\n\nfrom openapi_tester.schema_tester import SchemaTester\n\nschema_tester = SchemaTester()\n\n\nclass BaseAPITestCase(APITestCase):\n    \"\"\" Base test class for api views including schema validation \"\"\"\n\n    @staticmethod\n    def assertResponse(response: Response, **kwargs) -\u003e None:\n        \"\"\" helper to run validate_response and pass kwargs to it \"\"\"\n        schema_tester.validate_response(response=response, **kwargs)\n```\n\nThen use it in a test file:\n\n```python\nfrom shared.testing import BaseAPITestCase\n\n\nclass MyAPITests(BaseAPITestCase):\n    def test_some_view(self):\n        response = self.client.get(\"...\")\n        self.assertResponse(response)\n```\n\n## Options\n\nYou can pass options either globally, when instantiating a `SchemaTester`, or locally, when\ninvoking `validate_response`:\n\n```python\nfrom openapi_tester import SchemaTester, is_camel_case\nfrom tests.utils import my_uuid_4_validator\n\nschema_test_with_case_validation = SchemaTester(\n    case_tester=is_camel_case,\n    ignore_case=[\"IP\"],\n    validators=[my_uuid_4_validator]\n)\n\n```\n\nOr\n\n```python\nfrom openapi_tester import SchemaTester, is_camel_case\nfrom tests.utils import my_uuid_4_validator\n\nschema_tester = SchemaTester()\n\n\ndef my_test(client):\n    response = client.get('api/v1/test/1')\n    assert response.status_code == 200\n    schema_tester.validate_response(\n        response=response,\n        case_tester=is_camel_case,\n        ignore_case=[\"IP\"],\n        validators=[my_uuid_4_validator]\n    )\n```\n\n### case_tester\n\nThe case tester argument takes a callable that is used to validate the key case of both schemas and responses. If\nnothing is passed, case validation is skipped.\n\nThe library currently has 4 built-in case testers:\n\n- `is_pascal_case`\n- `is_snake_case`\n- `is_camel_case`\n- `is_kebab_case`\n\nYou can use one of these, or your own.\n\n### ignore_case\n\nList of keys to ignore when testing key case. This setting only applies when case_tester is not `None`.\n\n### validators\n\nList of custom validators. A validator is a function that receives two parameters: schema_section and data, and returns\neither an error message or `None`, e.g.:\n\n```python\nfrom typing import Any, Optional\nfrom uuid import UUID\n\n\ndef my_uuid_4_validator(schema_section: dict, data: Any) -\u003e Optional[str]:\n    schema_format = schema_section.get(\"format\")\n    if schema_format == \"uuid4\":\n        try:\n            result = UUID(data, version=4)\n            if not str(result) == str(data):\n                return f\"Expected uuid4, but received {data}\"\n        except ValueError:\n            return f\"Expected uuid4, but received {data}\"\n    return None\n```\n\n### field_key_map\n\nYou can pass an optional dictionary that maps custom url parameter names into values, for situations where this cannot be\ninferred by the DRF `EndpointEnumerator`. A concrete use case for this option is when\nthe [django i18n locale prefixes](https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#language-prefix-in-url-patterns).\n\n```python\nfrom openapi_tester import SchemaTester\n\nschema_tester = SchemaTester(field_key_map={\n  \"language\": \"en\",\n})\n```\n\n## Schema Validation\n\nWhen the SchemaTester loads a schema, it parses it using an\n[OpenAPI spec validator](https://github.com/p1c2u/openapi-spec-validator). This validates the schema.\nIn case of issues with the schema itself, the validator will raise the appropriate error.\n\n## Django testing client\n\nThe library includes an `OpenAPIClient`, which extends Django REST framework's\n[`APIClient` class](https://www.django-rest-framework.org/api-guide/testing/#apiclient).\nIf you wish to validate each response against OpenAPI schema when writing\nunit tests - `OpenAPIClient` is what you need!\n\nTo use `OpenAPIClient` simply pass `SchemaTester` instance that should be used\nto validate responses and then use it like regular Django testing client:\n\n```python\nschema_tester = SchemaTester()\nclient = OpenAPIClient(schema_tester=schema_tester)\nresponse = client.get('/api/v1/tests/123/')\n```\n\nTo force all developers working on the project to use `OpenAPIClient` simply\noverride the `client` fixture (when using `pytest` with `pytest-django`):\n\n```python\nfrom pytest_django.lazy_django import skip_if_no_django\n\nfrom openapi_tester.schema_tester import SchemaTester\n\n\n@pytest.fixture\ndef schema_tester():\n    return SchemaTester()\n\n\n@pytest.fixture\ndef client(schema_tester):\n    skip_if_no_django()\n\n    from openapi_tester.clients import OpenAPIClient\n\n    return OpenAPIClient(schema_tester=schema_tester)\n```\n\nIf you are using plain Django test framework, we suggest to create custom\ntest case implementation and use it instead of original Django one:\n\n```python\nimport functools\n\nfrom django.test.testcases import SimpleTestCase\nfrom openapi_tester.clients import OpenAPIClient\nfrom openapi_tester.schema_tester import SchemaTester\n\nschema_tester = SchemaTester()\n\n\nclass MySimpleTestCase(SimpleTestCase):\n    client_class = OpenAPIClient\n    # or use `functools.partial` when you want to provide custom\n    # ``SchemaTester`` instance:\n    # client_class = functools.partial(OpenAPIClient, schema_tester=schema_tester)\n```\n\nThis will ensure you all newly implemented views will be validated against\nthe OpenAPI schema.\n\n## Known Issues\n\n* We are using [prance](https://github.com/jfinkhaeuser/prance) as a schema resolver, and it has some issues with the\n  resolution of (very) complex OpenAPI 2.0 schemas. If you encounter\n  issues, [please document them here](https://github.com/snok/drf-openapi-tester/issues/205).\n\n## Contributing\n\nContributions are welcome. Please see the [contributing guide](https://github.com/snok/.github/blob/main/CONTRIBUTING.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsnok%2Fdrf-openapi-tester","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsnok%2Fdrf-openapi-tester","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsnok%2Fdrf-openapi-tester/lists"}