# drf-test-generator
[![Pypi Version](https://img.shields.io/pypi/v/drf-test-generator.svg?style=flat-square)](https://pypi.org/project/drf-test-generator/)
[![Supported Python Versions](https://img.shields.io/pypi/pyversions/drf-test-generator?style=flat-square)](https://pypi.org/project/drf-test-generator/)
[![Supported Django Versions](https://img.shields.io/pypi/frameworkversions/django/drf-test-generator?color=darkgreen&style=flat-square)](https://pypi.org/project/drf-test-generator/)
[![License](https://img.shields.io/github/license/saadmk11/drf-test-generator?style=flat-square)](https://github.com/saadmk11/drf-test-generator/blob/main/LICENSE)
![Django Tests](https://img.shields.io/github/actions/workflow/status/saadmk11/drf-test-generator/test.yaml?style=flat-square)
![Codecov](https://img.shields.io/codecov/c/github/saadmk11/drf-test-generator?style=flat-square&token=ugjHXbEKib)
![pre-commit.ci](https://img.shields.io/badge/pre--commit.ci-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square)
![Code Style](https://img.shields.io/badge/Code%20Style-Black-black?style=flat-square)
A Django Package that generates basic tests for Django REST Framework.
It can generate both `unittest` and `pytest` Style tests for all `ViewSets` in a Django REST Framework `router`.
# Features
- Generates tests for all `ViewSets` in a Django REST Framework `router`
- Generates tests for all custom `actions` in a `ViewSet`
- Generates tests for all `HTTP` methods in a `ViewSet`
- Supports test generation for both `pytest` and `unittest`
# Requirements
- Python 3.8+
- Django 4.2+
- Django REST Framework 3.12+
# Installation
```console
pip install drf-test-generator
```
# Setup
Add `drf_test_generator` to your `INSTALLED_APPS` in `settings.py`:
```python
INSTALLED_APPS = [
# ...
"drf_test_generator",
]
```
**Note:** This is required for the Django Management Command. You can remove it from `INSTALLED_APPS` once you have generated the tests.
# Generate Tests
## Django Management Command
You can generate tests using the Django Management Command:
```console
python manage.py generate_viewset_tests -r api.urls.router
```
**Note:** Currently this package only supports test generation for ViewSets that are registered in a router.
## Django Management Command Options
### `-r` or `--router` (REQUIRED)
The dotted path to the REST framework url router. This is the router that contains the ViewSets you want to generate tests for.
**Example:** `--router api.urls.router`
### `--test-base-class`
The dotted path to the test base class. This is the class that your tests will inherit from. If not provided, the tests will inherit from `rest_framework.test.APITestCase`.
**Example:** `--test-base-class tests.base.MyCustomTest`
### `--namespace`
The namespace to use for the URLs (e.g: `api_v1:posts-list` ). If not provided, no namespace will be used.
**Example:** `--namespace api_v1`
### `--output-file`
The path to the output file. If not provided, the tests will be printed to the console.
**Example:** `--output-file tests.py`
### `--select-viewsets`
A list of ViewSets to generate tests for. If not provided, tests will be generated for all ViewSets in the router.
**Example:** `--select-viewsets PostViewSet CommentViewSet`
### `--variant`
The test variant to generate. Options: `pytest`, `unittest`. If not provided, `unittest` will be used.
**Example:** `--variant pytest`
### `--pytest-markers`
A list of pytest markers to add to the generated tests. If not provided, only the `@pytest.mark.django_db` marker will be added.
**Example:** `--pytest-markers pytest.mark.ignore_template_errors pytest.mark.urls('myapp.test_urls')`
### `--pytest-fixtures`
A list of pytest fixtures to add to the generated tests. If not provided, only the `client` fixture will be added.
**Example:** `--pytest-fixtures django_user_model`
# Examples Usage
## For the following code
```python
# api/views.py
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
class PostViewSet(viewsets.ModelViewSet):
# ...
@action(detail=False, methods=["get", "post"], url_path="custom-action")
def custom_action(self, request):
return Response()
@action(detail=True, methods=["get", "options", "put"], url_path="new-action")
def custom_action2(self, request):
return Response()
# api/urls.py
from rest_framework import routers
router = routers.DefaultRouter()
router.register("posts", PostViewSet, basename="post")
```
## For Unittest
**Run Command**
```console
python manage.py generate_viewset_tests -r api.urls.router
```
**Output**
```python
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
class PostViewSetTests(APITestCase):
def test_post_list_get(self):
url = reverse("post-list")
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_post_create_post(self):
url = reverse("post-list")
response = self.client.post(url, data={})
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
def test_post_custom_action_get(self):
url = reverse("post-custom-action")
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_post_custom_action_post(self):
url = reverse("post-custom-action")
response = self.client.post(url, data={})
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
def test_post_retrieve_get(self):
url = reverse("post-detail", kwargs={"pk": None})
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_post_update_put(self):
url = reverse("post-detail", kwargs={"pk": None})
response = self.client.put(url, data={})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_post_partial_update_patch(self):
url = reverse("post-detail", kwargs={"pk": None})
response = self.client.patch(url, data={})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_post_destroy_delete(self):
url = reverse("post-detail", kwargs={"pk": None})
response = self.client.delete(url)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
def test_post_custom_action2_get(self):
url = reverse("post-custom-action2", kwargs={"pk": None})
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_post_custom_action2_options(self):
url = reverse("post-custom-action2", kwargs={"pk": None})
response = self.client.options(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_post_custom_action2_put(self):
url = reverse("post-custom-action2", kwargs={"pk": None})
response = self.client.put(url, data={})
self.assertEqual(response.status_code, status.HTTP_200_OK)
```
## For Pytest
**Run Command**
```console
python manage.py generate_viewset_tests -r api.urls.router --variant pytest
```
**Output**
```python
from django.urls import reverse
from rest_framework import status
import pytest
# PostViewSet Tests
@pytest.mark.django_db
def test_post_list_get(client):
url = reverse("post-list")
response = client.get(url)
assert response.status_code == status.HTTP_200_OK
@pytest.mark.django_db
def test_post_create_post(client):
url = reverse("post-list")
response = client.post(url, data={})
assert response.status_code == status.HTTP_201_CREATED
@pytest.mark.django_db
def test_post_custom_action_get(client):
url = reverse("post-custom-action")
response = client.get(url)
assert response.status_code == status.HTTP_200_OK
@pytest.mark.django_db
def test_post_custom_action_post(client):
url = reverse("post-custom-action")
response = client.post(url, data={})
assert response.status_code == status.HTTP_201_CREATED
@pytest.mark.django_db
def test_post_retrieve_get(client):
url = reverse("post-detail", kwargs={"pk": None})
response = client.get(url)
assert response.status_code == status.HTTP_200_OK
@pytest.mark.django_db
def test_post_update_put(client):
url = reverse("post-detail", kwargs={"pk": None})
response = client.put(url, data={})
assert response.status_code == status.HTTP_200_OK
@pytest.mark.django_db
def test_post_partial_update_patch(client):
url = reverse("post-detail", kwargs={"pk": None})
response = client.patch(url, data={})
assert response.status_code == status.HTTP_200_OK
@pytest.mark.django_db
def test_post_destroy_delete(client):
url = reverse("post-detail", kwargs={"pk": None})
response = client.delete(url)
assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.django_db
def test_post_custom_action2_get(client):
url = reverse("post-custom-action2", kwargs={"pk": None})
response = client.get(url)
assert response.status_code == status.HTTP_200_OK
@pytest.mark.django_db
def test_post_custom_action2_options(client):
url = reverse("post-custom-action2", kwargs={"pk": None})
response = client.options(url)
assert response.status_code == status.HTTP_200_OK
@pytest.mark.django_db
def test_post_custom_action2_put(client):
url = reverse("post-custom-action2", kwargs={"pk": None})
response = client.put(url, data={})
assert response.status_code == status.HTTP_200_OK
```
# License
The code in this project is released under the [MIT License](LICENSE).
Raw data
{
"_id": null,
"home_page": "https://github.com/saadmk11/drf-test-generator",
"name": "drf-test-generator",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "Django, Django Rest Framework, Testing",
"author": "Maksudul Haque",
"author_email": "saad.mk112@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/32/cd/3a6caca1f845ab1c76963c5f87aef9df09dca769bae173674240ec9e2fa0/drf_test_generator-0.0.6.tar.gz",
"platform": null,
"description": "# drf-test-generator\n\n[![Pypi Version](https://img.shields.io/pypi/v/drf-test-generator.svg?style=flat-square)](https://pypi.org/project/drf-test-generator/)\n[![Supported Python Versions](https://img.shields.io/pypi/pyversions/drf-test-generator?style=flat-square)](https://pypi.org/project/drf-test-generator/)\n[![Supported Django Versions](https://img.shields.io/pypi/frameworkversions/django/drf-test-generator?color=darkgreen&style=flat-square)](https://pypi.org/project/drf-test-generator/)\n[![License](https://img.shields.io/github/license/saadmk11/drf-test-generator?style=flat-square)](https://github.com/saadmk11/drf-test-generator/blob/main/LICENSE)\n\n![Django Tests](https://img.shields.io/github/actions/workflow/status/saadmk11/drf-test-generator/test.yaml?style=flat-square)\n![Codecov](https://img.shields.io/codecov/c/github/saadmk11/drf-test-generator?style=flat-square&token=ugjHXbEKib)\n![pre-commit.ci](https://img.shields.io/badge/pre--commit.ci-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square)\n![Code Style](https://img.shields.io/badge/Code%20Style-Black-black?style=flat-square)\n\nA Django Package that generates basic tests for Django REST Framework.\nIt can generate both `unittest` and `pytest` Style tests for all `ViewSets` in a Django REST Framework `router`.\n\n# Features\n\n- Generates tests for all `ViewSets` in a Django REST Framework `router`\n- Generates tests for all custom `actions` in a `ViewSet`\n- Generates tests for all `HTTP` methods in a `ViewSet`\n- Supports test generation for both `pytest` and `unittest`\n\n# Requirements\n\n- Python 3.8+\n- Django 4.2+\n- Django REST Framework 3.12+\n\n# Installation\n\n```console\npip install drf-test-generator\n```\n\n# Setup\n\nAdd `drf_test_generator` to your `INSTALLED_APPS` in `settings.py`:\n\n```python\nINSTALLED_APPS = [\n # ...\n \"drf_test_generator\",\n]\n```\n\n**Note:** This is required for the Django Management Command. You can remove it from `INSTALLED_APPS` once you have generated the tests.\n\n# Generate Tests\n\n## Django Management Command\n\nYou can generate tests using the Django Management Command:\n\n```console\npython manage.py generate_viewset_tests -r api.urls.router\n```\n\n**Note:** Currently this package only supports test generation for ViewSets that are registered in a router.\n\n## Django Management Command Options\n\n### `-r` or `--router` (REQUIRED)\n\nThe dotted path to the REST framework url router. This is the router that contains the ViewSets you want to generate tests for.\n\n**Example:** `--router api.urls.router`\n\n### `--test-base-class`\n\nThe dotted path to the test base class. This is the class that your tests will inherit from. If not provided, the tests will inherit from `rest_framework.test.APITestCase`.\n\n**Example:** `--test-base-class tests.base.MyCustomTest`\n\n### `--namespace`\n\nThe namespace to use for the URLs (e.g: `api_v1:posts-list` ). If not provided, no namespace will be used.\n\n**Example:** `--namespace api_v1`\n\n### `--output-file`\n\nThe path to the output file. If not provided, the tests will be printed to the console.\n\n**Example:** `--output-file tests.py`\n\n### `--select-viewsets`\n\nA list of ViewSets to generate tests for. If not provided, tests will be generated for all ViewSets in the router.\n\n**Example:** `--select-viewsets PostViewSet CommentViewSet`\n\n### `--variant`\n\nThe test variant to generate. Options: `pytest`, `unittest`. If not provided, `unittest` will be used.\n\n**Example:** `--variant pytest`\n\n### `--pytest-markers`\n\nA list of pytest markers to add to the generated tests. If not provided, only the `@pytest.mark.django_db` marker will be added.\n\n**Example:** `--pytest-markers pytest.mark.ignore_template_errors pytest.mark.urls('myapp.test_urls')`\n\n### `--pytest-fixtures`\n\nA list of pytest fixtures to add to the generated tests. If not provided, only the `client` fixture will be added.\n\n**Example:** `--pytest-fixtures django_user_model`\n\n# Examples Usage\n\n## For the following code\n\n```python\n# api/views.py\nfrom rest_framework import viewsets\nfrom rest_framework.decorators import action\nfrom rest_framework.response import Response\n\n\nclass PostViewSet(viewsets.ModelViewSet):\n # ...\n @action(detail=False, methods=[\"get\", \"post\"], url_path=\"custom-action\")\n def custom_action(self, request):\n return Response()\n\n @action(detail=True, methods=[\"get\", \"options\", \"put\"], url_path=\"new-action\")\n def custom_action2(self, request):\n return Response()\n\n\n# api/urls.py\nfrom rest_framework import routers\n\nrouter = routers.DefaultRouter()\n\nrouter.register(\"posts\", PostViewSet, basename=\"post\")\n```\n\n## For Unittest\n\n**Run Command**\n\n```console\npython manage.py generate_viewset_tests -r api.urls.router\n```\n\n**Output**\n\n```python\nfrom django.urls import reverse\n\nfrom rest_framework import status\n\nfrom rest_framework.test import APITestCase\n\n\nclass PostViewSetTests(APITestCase):\n def test_post_list_get(self):\n url = reverse(\"post-list\")\n response = self.client.get(url)\n self.assertEqual(response.status_code, status.HTTP_200_OK)\n\n def test_post_create_post(self):\n url = reverse(\"post-list\")\n response = self.client.post(url, data={})\n self.assertEqual(response.status_code, status.HTTP_201_CREATED)\n\n def test_post_custom_action_get(self):\n url = reverse(\"post-custom-action\")\n response = self.client.get(url)\n self.assertEqual(response.status_code, status.HTTP_200_OK)\n\n def test_post_custom_action_post(self):\n url = reverse(\"post-custom-action\")\n response = self.client.post(url, data={})\n self.assertEqual(response.status_code, status.HTTP_201_CREATED)\n\n def test_post_retrieve_get(self):\n url = reverse(\"post-detail\", kwargs={\"pk\": None})\n response = self.client.get(url)\n self.assertEqual(response.status_code, status.HTTP_200_OK)\n\n def test_post_update_put(self):\n url = reverse(\"post-detail\", kwargs={\"pk\": None})\n response = self.client.put(url, data={})\n self.assertEqual(response.status_code, status.HTTP_200_OK)\n\n def test_post_partial_update_patch(self):\n url = reverse(\"post-detail\", kwargs={\"pk\": None})\n response = self.client.patch(url, data={})\n self.assertEqual(response.status_code, status.HTTP_200_OK)\n\n def test_post_destroy_delete(self):\n url = reverse(\"post-detail\", kwargs={\"pk\": None})\n response = self.client.delete(url)\n self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)\n\n def test_post_custom_action2_get(self):\n url = reverse(\"post-custom-action2\", kwargs={\"pk\": None})\n response = self.client.get(url)\n self.assertEqual(response.status_code, status.HTTP_200_OK)\n\n def test_post_custom_action2_options(self):\n url = reverse(\"post-custom-action2\", kwargs={\"pk\": None})\n response = self.client.options(url)\n self.assertEqual(response.status_code, status.HTTP_200_OK)\n\n def test_post_custom_action2_put(self):\n url = reverse(\"post-custom-action2\", kwargs={\"pk\": None})\n response = self.client.put(url, data={})\n self.assertEqual(response.status_code, status.HTTP_200_OK)\n```\n\n## For Pytest\n\n**Run Command**\n\n```console\npython manage.py generate_viewset_tests -r api.urls.router --variant pytest\n```\n\n**Output**\n\n```python\nfrom django.urls import reverse\n\nfrom rest_framework import status\n\nimport pytest\n\n# PostViewSet Tests\n\n\n@pytest.mark.django_db\ndef test_post_list_get(client):\n url = reverse(\"post-list\")\n response = client.get(url)\n assert response.status_code == status.HTTP_200_OK\n\n\n@pytest.mark.django_db\ndef test_post_create_post(client):\n url = reverse(\"post-list\")\n response = client.post(url, data={})\n assert response.status_code == status.HTTP_201_CREATED\n\n\n@pytest.mark.django_db\ndef test_post_custom_action_get(client):\n url = reverse(\"post-custom-action\")\n response = client.get(url)\n assert response.status_code == status.HTTP_200_OK\n\n\n@pytest.mark.django_db\ndef test_post_custom_action_post(client):\n url = reverse(\"post-custom-action\")\n response = client.post(url, data={})\n assert response.status_code == status.HTTP_201_CREATED\n\n\n@pytest.mark.django_db\ndef test_post_retrieve_get(client):\n url = reverse(\"post-detail\", kwargs={\"pk\": None})\n response = client.get(url)\n assert response.status_code == status.HTTP_200_OK\n\n\n@pytest.mark.django_db\ndef test_post_update_put(client):\n url = reverse(\"post-detail\", kwargs={\"pk\": None})\n response = client.put(url, data={})\n assert response.status_code == status.HTTP_200_OK\n\n\n@pytest.mark.django_db\ndef test_post_partial_update_patch(client):\n url = reverse(\"post-detail\", kwargs={\"pk\": None})\n response = client.patch(url, data={})\n assert response.status_code == status.HTTP_200_OK\n\n\n@pytest.mark.django_db\ndef test_post_destroy_delete(client):\n url = reverse(\"post-detail\", kwargs={\"pk\": None})\n response = client.delete(url)\n assert response.status_code == status.HTTP_204_NO_CONTENT\n\n\n@pytest.mark.django_db\ndef test_post_custom_action2_get(client):\n url = reverse(\"post-custom-action2\", kwargs={\"pk\": None})\n response = client.get(url)\n assert response.status_code == status.HTTP_200_OK\n\n\n@pytest.mark.django_db\ndef test_post_custom_action2_options(client):\n url = reverse(\"post-custom-action2\", kwargs={\"pk\": None})\n response = client.options(url)\n assert response.status_code == status.HTTP_200_OK\n\n\n@pytest.mark.django_db\ndef test_post_custom_action2_put(client):\n url = reverse(\"post-custom-action2\", kwargs={\"pk\": None})\n response = client.put(url, data={})\n assert response.status_code == status.HTTP_200_OK\n```\n\n# License\n\nThe code in this project is released under the [MIT License](LICENSE).\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A Django app to generate basic tests for Django Rest Framework",
"version": "0.0.6",
"project_urls": {
"Homepage": "https://github.com/saadmk11/drf-test-generator"
},
"split_keywords": [
"django",
" django rest framework",
" testing"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "29d82ec621fe4fcd3c81e6d208e684d54beaf25f40b56c6958b8bffa73f825e3",
"md5": "c1d6d2316b8c076001351669f06061f5",
"sha256": "ed1ca1bbb7fd00613889802dcf55ba5c4a11b5cd8fa2f1f91af1979c8023fe0f"
},
"downloads": -1,
"filename": "drf_test_generator-0.0.6-py3-none-any.whl",
"has_sig": false,
"md5_digest": "c1d6d2316b8c076001351669f06061f5",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 9285,
"upload_time": "2024-09-07T14:04:49",
"upload_time_iso_8601": "2024-09-07T14:04:49.545491Z",
"url": "https://files.pythonhosted.org/packages/29/d8/2ec621fe4fcd3c81e6d208e684d54beaf25f40b56c6958b8bffa73f825e3/drf_test_generator-0.0.6-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "32cd3a6caca1f845ab1c76963c5f87aef9df09dca769bae173674240ec9e2fa0",
"md5": "4c0246f8ac515bfee2864798ebc354dd",
"sha256": "e487d23d98de22559d2c69d4bd915b373b0d5252fa36aedf4dd09e1fa458b6b8"
},
"downloads": -1,
"filename": "drf_test_generator-0.0.6.tar.gz",
"has_sig": false,
"md5_digest": "4c0246f8ac515bfee2864798ebc354dd",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 12275,
"upload_time": "2024-09-07T14:04:50",
"upload_time_iso_8601": "2024-09-07T14:04:50.894068Z",
"url": "https://files.pythonhosted.org/packages/32/cd/3a6caca1f845ab1c76963c5f87aef9df09dca769bae173674240ec9e2fa0/drf_test_generator-0.0.6.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-09-07 14:04:50",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "saadmk11",
"github_project": "drf-test-generator",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "drf-test-generator"
}