Closed skarzi closed 2 years ago
Hi!
That sounds interesting. How would it look?
Something like this works for me nicely:
from typing import Optional
from django.conf import settings
from openapi_tester.schema_tester import SchemaTester
from rest_framework.response import Response
from rest_framework.test import APIClient
class OpenAPIClient(APIClient):
"""``APIClient`` validating responses against OpenAPI description."""
def __init__(
self,
*args,
openapi_description_tester: Optional[SchemaTester] = None,
**kwargs,
) -> None:
"""Initialize ``OpenAPIClient`` instance."""
super().__init__(*args, **kwargs)
self.openapi_description_tester = (
openapi_description_tester
or self._default_openapi_description_tester_factory()
)
def request(self, **kwargs) -> Response: # type: ignore[override]
"""Validate fetched response against given OpenAPI description."""
response = super().request(**kwargs)
self.openapi_description_tester.validate_response(response)
return response
def _default_openapi_description_tester_factory(self) -> SchemaTester:
"""Initialize default ``SchemaTester`` instance."""
return SchemaTester(
schema_file_path=settings.OPENAPI_DESCRIPTION_FILEPATH,
)
Of course, we can make it more generic by requiring passing openapi_description_tester
when initializing this client.
Just so I completely understand then, using this in tests would look something like this?
schema_tester = SchemaTester()
client = OpenAPIClient(tester=schema_tester)
response = client.request('GET', '/api/v1/test')
instead of
schema_tester = SchemaTester()
response = client.get('/api/v1/test') # 'client' if included as a fixture in pytest tests, 'self.client' in TestCase
schema_tester.validate_response(response=response)
This seems equally verbose. Are there specific cases where you would save lines of code, or make things less complex by (re-)using the client instead of the schema tester instance directly? 🙂
Yes, however, you can still use client.get(...)
etc, because these methods use request
under the hood. Additionally, you can enforce all developers working on the project to use OpenAPIClient
by simply overriding the client
fixture (or defining and using a new, custom fixture e.g. openapi_client
), then you will be sure all newly implemented views will be validated against the OpenAPI description. So then it will look like a regular view test:
response = client.get('api/v1/test')
or when using openapi_client
fixture instead of overriding client
:
response = openapi_client.get('api/v1/test')
Neat! I'd be happy to accept a PR for this then 🙂
One tiny nitpick for the implementation: I think we should try to use "openapi schema" instead of "openapi description" to be consistent with how we've otherwise referenced it in the package.
I think documenting exactly what you wrote above is also very valuable if you're up for adding that to the readme 👍
Hi :wave:
First of all big thanks for developing this great package! I have been using
drf-openapi-tester
in a few Django-based projects and it works like a charm, however, usually, I'm adding the same code to every project -rest_framework.client.APIClient
subclass that simply validates response against OpenAPI description usingSchemaTester
. What do you think about adding such a client todrf-openapi-tester
package?