Show HN: drf-api-action - A new way to test you rest-framework project
GitHub - Ori-Roza/drf-api-action: drf-api-action elevates Django Rest Framework testing with the action_api fixture, simplifying REST endpoint testing to a seamless, function-like experience.




The drf-api-action Python package is designed to elevate your testing experience for Django Rest Framework (DRF) REST endpoints.
With the api_action fixture, this package empowers you to effortlessly test your REST endpoints as if they were conventional functions.
Features:


Simplified Testing: Testing DRF REST endpoints using the api_action plugin, treating them like regular functions.


Seamless Integration: you don't need to do anything in existing server code.


Easy Debugging: Instead of getting a response with error code by using default drf testing client object , get the real traceback that led to the error.


Pagination Support: Paginating easily through pages by using page argument.


Installation
You can install drf-api-action using pip:
pip install drf-api-action
Usage
To use drf-api-action as a Pytest fixture, you need to follow these steps:
Step 1: Import your Viewsets explicitly:
import pytest
from tests.test_server.test_app.models import DummyModel
from tests.test_server.test_app.views import DummyViewSetFixture
Step 2: use the following action_api mark decorator:
@pytest.mark.api_action(view_set_class={YOUR VIEW_SET})
e.g:
our ViewSet is called DummyViewSetFixture
import pytest
from tests.test_server.test_app.views import DummyViewSetFixture


@pytest.mark.api_action(view_set_class=DummyViewSetFixture)
def test_call_as_api_fixture(db, api_action):
  pass
Now you can use all DummyViewSetFixture functionality!
Step 3: write your tests
e.g:
our ViewSet is called DummyViewSetFixture
import pytest
from tests.test_server.test_app.models import DummyModel
from tests.test_server.test_app.views import DummyViewSetFixture


@pytest.mark.api_action(view_set_class=DummyViewSetFixture)
def test_call_as_api_fixture(db, api_action):
  dummy_model = DummyModel()
  dummy_model.dummy_int = 1
  dummy_model.save()
  res = api_action.api_dummy(pk=1)
  assert res["dummy_int"] == 1
Here as an example, the real exception and trace will be thrown, make it easy to understand what the issue is:
import pytest
from tests.test_server.test_app.views import DummyViewSetFixture


@pytest.mark.api_action(view_set_class=DummyViewSetFixture)
def test_dummy(db, api_action):
  result = api_action.dummy(pk='bbb')
  assert result['dummy_int'] == 1
tests/functionality_tests/test_as_api.py:11 (test_call_as_api)
self = <django.db.models.fields.BigAutoField: id>, value = 'bb'

    def get_prep_value(self, value):
        value = super().get_prep_value(value)
        if value is None:
            return None
        try:
>           return int(value)
E           ValueError: invalid literal for int() with base 10: 'bb'

../venv/lib/python3.9/site-packages/django/db/models/fields/__init__.py:2053: ValueError

The above exception was the direct cause of the following exception:

queryset = <QuerySet [<DummyModel: DummyModel object (1)>]>, filter_args = ()
filter_kwargs = {'pk': 'bb'}

    def get_object_or_404(queryset, *filter_args, **filter_kwargs):
        """
        Same as Django's standard shortcut, but make sure to also raise 404
        if the filter_kwargs don't match the required types.
        """
        try:
>           return _get_object_or_404(queryset, *filter_args, **filter_kwargs)

../venv/lib/python3.9/site-packages/rest_framework/generics.py:19: 
....
....
...
Call endpoints with pagination:
@pytest.mark.api_action(view_set_class=DummyAPIViewSet)
def test_pagination_data(db, api_action):
    for i in range(1, 3):
        dummy_model = DummyModel()
        dummy_model.dummy_int = 1
        dummy_model.save()

    response = api_action.by_dummy_int(dummy_int=1, page=1)

    obj = response['results'][0]
    assert obj['dummy_int'] == 1

    assert extract_page_number(response['next']) == 2

    response = api_action.by_dummy_int(dummy_int=1, page=2)
    assert extract_page_number(response['previous']) == 1
    assert extract_page_number(response['next']) is None
Package Testing
The drf-api-action library includes tests to ensure the functionality works as expected. To run the tests run pytest:

The tests will be executed, and the results will be displayed in the console.
Example
Example of using drf-api-action in a DRF project
Support & Contribution
For guidance on support & contribution, see the contributing guidelines.
Bug Report
For guidance on how to open a bug, see the bug report template.
Open an Issue
For guidance on how to open an issue, see the issue template.
Code Of Conduct
code of conduct.
