Skip to content
This repository has been archived by the owner on Nov 19, 2023. It is now read-only.

custom testing API client implementation #261

Closed
skarzi opened this issue Apr 12, 2022 · 6 comments · Fixed by #262
Closed

custom testing API client implementation #261

skarzi opened this issue Apr 12, 2022 · 6 comments · Fixed by #262

Comments

@skarzi
Copy link
Contributor

skarzi commented Apr 12, 2022

Hi 👋

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 using SchemaTester.
What do you think about adding such a client to drf-openapi-tester package?

@sondrelg
Copy link
Member

Hi!

That sounds interesting. How would it look?

@skarzi
Copy link
Contributor Author

skarzi commented Apr 12, 2022

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.

@sondrelg
Copy link
Member

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? 🙂

@skarzi
Copy link
Contributor Author

skarzi commented Apr 12, 2022

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')

@sondrelg
Copy link
Member

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 👍

@skarzi
Copy link
Contributor Author

skarzi commented Apr 13, 2022

great, so I will prepare PR in the following days!
Sure I will use "schema" instead of "description".
BTW I'm using "description" mostly because of this glossary.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants