{"id":14972794,"url":"https://github.com/hbakri/django-rest-testing","last_synced_at":"2025-08-31T22:41:35.565Z","repository":{"id":239489050,"uuid":"799204564","full_name":"hbakri/django-rest-testing","owner":"hbakri","description":"☔️ Declarative Tests for RESTful APIs with Django.","archived":false,"fork":false,"pushed_at":"2024-12-16T10:29:22.000Z","size":185,"stargazers_count":16,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-13T13:07:28.963Z","etag":null,"topics":["api","django","python","rest","testing"],"latest_commit_sha":null,"homepage":"https://django-rest-testing.readme.io","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hbakri.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"hbakri"}},"created_at":"2024-05-11T13:00:12.000Z","updated_at":"2024-12-22T08:41:15.000Z","dependencies_parsed_at":null,"dependency_job_id":"0cb1d2bc-aa8a-4fd7-95c9-1fe47ece20b3","html_url":"https://github.com/hbakri/django-rest-testing","commit_stats":{"total_commits":5,"total_committers":1,"mean_commits":5.0,"dds":0.0,"last_synced_commit":"e5b52e90d5eadbac7697be78e9193315b9c5044e"},"previous_names":["hbakri/django-rest-testing"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/hbakri/django-rest-testing","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hbakri%2Fdjango-rest-testing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hbakri%2Fdjango-rest-testing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hbakri%2Fdjango-rest-testing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hbakri%2Fdjango-rest-testing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hbakri","download_url":"https://codeload.github.com/hbakri/django-rest-testing/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hbakri%2Fdjango-rest-testing/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273051861,"owners_count":25037074,"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","status":"online","status_checked_at":"2025-08-31T02:00:09.071Z","response_time":79,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","django","python","rest","testing"],"created_at":"2024-09-24T13:47:33.119Z","updated_at":"2025-08-31T22:41:35.547Z","avatar_url":"https://github.com/hbakri.png","language":"Python","funding_links":["https://github.com/sponsors/hbakri","https://www.buymeacoffee.com/hbakri"],"categories":[],"sub_categories":[],"readme":"# Django REST Testing\n[![Tests](https://github.com/hbakri/django-rest-testing/actions/workflows/tests.yml/badge.svg)](https://github.com/hbakri/django-rest-testing/actions)\n[![Coverage](https://img.shields.io/codecov/c/github/hbakri/django-rest-testing/main.svg?label=coverage\u0026logo=codecov\u0026logoColor=white)](https://codecov.io/gh/hbakri/django-rest-testing)\n[![PyPI version](https://img.shields.io/pypi/v/django-rest-testing?color=blue\u0026logo=pypi\u0026logoColor=white)](https://pypi.org/project/django-rest-testing/)\n[![Downloads](https://static.pepy.tech/badge/django-rest-testing/month)](https://pepy.tech/project/django-rest-testing)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![MyPy](https://img.shields.io/badge/mypy-checked-blue.svg)](https://github.com/python/mypy)\n[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)\n\n![Django REST Testing](https://raw.githubusercontent.com/hbakri/django-rest-testing/main/docs/assets/images/django-rest-testing-logo.png)\n\n**Django REST Testing** is a small, [declarative](https://en.wikipedia.org/wiki/Declarative_programming), opinionated, and yet powerful tool designed to streamline the development of tests for **RESTful** endpoints within [Django](https://github.com/django/django). This package embraces best practices to ensure efficient and robust endpoint testing, allowing developers to focus on what truly matters when testing their applications: ensuring they work as expected.\n\nOriginally integrated within [Django Ninja CRUD](https://github.com/hbakri/django-ninja-crud), it has evolved into a standalone package. This evolution enables developers to test their RESTful endpoints with ease and precision, regardless of the framework in use.\n\nBy using a **scenario-based** test case approach, this package empowers developers to\nrigorously test RESTful endpoints under varied conditions and inputs. Each scenario\nspecifically targets distinct endpoint behaviors—ranging from handling valid and\ninvalid inputs to managing nonexistent resources and enforcing business rules.\n\nThis modular approach breaks tests into distinct, manageable units, streamlining the testing\nprocess, enhancing clarity and maintainability, and ensuring comprehensive\ncoverage — making it an **indispensable tool** for modern web development.\n\n## 📝 Requirements\n\n[![Python versions](https://img.shields.io/pypi/pyversions/django-rest-testing.svg?color=306998\u0026label=python\u0026logo=python\u0026logoColor=white)](https://github.com/python/cpython)\n[![Django versions](https://img.shields.io/badge/3.2_|_4.1_|_4.2_|_5.0-blue?color=0C4B33\u0026label=django\u0026logo=django\u0026logoColor=white)](https://github.com/django/django)\n[![Pydantic versions](https://img.shields.io/badge/\u003e=2.0-blue?color=black\u0026label=pydantic\u0026logo=pydantic\u0026logoColor=white)](https://github.com/vitalik/django-ninja)\n\n## ⚒️ Installation\n```bash\npip install django-rest-testing\n```\nFor more information, see the [installation guide](https://django-rest-testing.readme.io/docs/02-installation).\n\n## 👨‍🎨 Example\nLet's imagine you're building a system for a university and you have a model called `Department`. Each department in your university has a unique title.\n\n```python\n# examples/models.py\nimport uuid\nfrom django.db import models\n\nclass Department(models.Model):\n    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)\n    title = models.CharField(max_length=255, unique=True)\n```\n\nTo interact with this data, we need a way to convert it between Python objects and a\nformat that's easy to read and write (like JSON). We can use [Pydantic](\nhttps://github.com/pydantic/pydantic) to define schemas for our data:\n\n```python\n# examples/schemas.py\nimport uuid\nfrom pydantic import BaseModel\n\nclass DepartmentIn(BaseModel):\n    title: str\n\nclass DepartmentOut(BaseModel):\n    id: uuid.UUID\n    title: str\n```\n\nThe `DepartmentIn` schema defines what data we need when creating or updating a department.\nThe `DepartmentOut` schema defines what data we'll provide when retrieving a department.\n\nNow, we take pride in the simplicity and directness of using vanilla Django to\nhandle our endpoints. It’s like cooking a gourmet meal with just a few basic\ningredients — surprisingly satisfying and impressively functional.\n\n```python\n# examples/views.py\nimport uuid\n\nfrom django.http import HttpRequest, HttpResponse\nfrom django.views.decorators.http import require_http_methods\n\nfrom examples.models import Department\nfrom examples.schemas import DepartmentIn, DepartmentOut\n\n\n@require_http_methods([\"GET\", \"PUT\", \"DELETE\"])\ndef read_update_delete_department(request: HttpRequest, id: uuid.UUID):\n    department = Department.objects.get(id=id)\n\n    if request.method == \"GET\":\n        response_body = DepartmentOut.model_validate(department, from_attributes=True)\n        return HttpResponse(content=response_body.model_dump_json(), status=200)\n\n    elif request.method == \"PUT\":\n        request_body = DepartmentIn.model_validate_json(request.body)\n        for key, value in request_body.dict().items():\n            setattr(department, key, value)\n\n        department.full_clean()\n        department.save()\n        response_body = DepartmentOut.model_validate(department, from_attributes=True)\n        return HttpResponse(content=response_body.model_dump_json(), status=200)\n\n    elif request.method == \"DELETE\":\n        department.delete()\n        return HttpResponse(content=b\"\", status=204)\n```\n\nThere you have it—a minimalistic yet powerful approach to handling RESTful operations\nin Django. Up next, let’s dive into how declarative testing makes validating these\nendpoints both efficient and straightforward.\n\n```python\n# examples/tests.py\nimport uuid\n\nfrom examples.models import Department\nfrom examples.schemas import DepartmentOut\n\nfrom rest_testing import APITestCase, APIViewTestScenario\n\n\nclass TestDepartmentViewSet(APITestCase):\n    department_1: Department\n    department_2: Department\n\n    @classmethod\n    def setUpTestData(cls):\n        cls.department_1 = Department.objects.create(title=\"department-1\")\n        cls.department_2 = Department.objects.create(title=\"department-2\")\n\n    def test_read_department(self):\n        self.assertScenariosSucceed(\n            method=\"GET\",\n            path=\"/api/departments/{id}\",\n            scenarios=[\n                APIViewTestScenario(\n                    path_parameters={\"id\": self.department_1.id},\n                    expected_response_status=200,\n                    expected_response_body_type=DepartmentOut,\n                    expected_response_body={\n                        \"id\": str(self.department_1.id),\n                        \"title\": self.department_1.title,\n                    },\n                ),\n                APIViewTestScenario(\n                    path_parameters={\"id\": uuid.uuid4()},\n                    expected_response_status=404,\n                ),\n            ],\n        )\n\n    def test_update_department(self):\n        self.assertScenariosSucceed(\n            method=\"PUT\",\n            path=\"/api/departments/{id}\",\n            scenarios=[\n                APIViewTestScenario(\n                    path_parameters={\"id\": self.department_1.id},\n                    request_body={\"title\": \"new_title\"},\n                    expected_response_status=200,\n                    expected_response_body_type=DepartmentOut,\n                    expected_response_body={\n                        \"id\": str(self.department_1.id),\n                        \"title\": \"new_title\",\n                    },\n                ),\n                APIViewTestScenario(\n                    path_parameters={\"id\": uuid.uuid4()},\n                    request_body={\"title\": \"new_title\"},\n                    expected_response_status=404,\n                ),\n                APIViewTestScenario(\n                    path_parameters={\"id\": self.department_1.id},\n                    request_body={\"title\": [1]},\n                    expected_response_status=400,\n                ),\n                APIViewTestScenario(\n                    path_parameters={\"id\": self.department_1.id},\n                    request_body={\"title\": self.department_2.title},\n                    expected_response_status=400,\n                ),\n            ],\n        )\n\n    def test_delete_department(self):\n        self.assertScenariosSucceed(\n            method=\"DELETE\",\n            path=\"/api/departments/{id}\",\n            scenarios=[\n                APIViewTestScenario(\n                    path_parameters={\"id\": self.department_1.id},\n                    expected_response_status=204,\n                    expected_response_body=b\"\",\n                ),\n                APIViewTestScenario(\n                    path_parameters={\"id\": uuid.uuid4()},\n                    expected_response_status=404,\n                ),\n            ],\n        )\n```\n\nAs you can see, the `APITestCase` class provides a simple and intuitive way to\ndefine test scenarios. Each scenario specifies the expected request and response,\nmaking it easy to understand what's being tested. This approach not only simplifies\nthe testing process but also enhances the **clarity** and **maintainability** of\ntest suites.\n\n## 📚 Documentation\nFor more information, see the [documentation](https://django-rest-testing.readme.io/).\n\n## 🫶 Support\nFirst and foremost, a heartfelt thank you for taking an interest in this project. If it has been helpful to you or you believe in its potential, kindly consider giving it a star on GitHub. Such recognition not only fuels my drive to maintain and improve this work but also makes it more visible to new potential users and contributors.\n\n![GitHub Repo stars](https://img.shields.io/github/stars/hbakri/django-rest-testing?style=social)\n\nIf you've benefited from this project or appreciate the dedication behind it, consider showing further support. Whether it's the price of a coffee, a word of encouragement, or a sponsorship, every gesture adds fuel to the open-source fire, making it shine even brighter. ✨\n\n[![Sponsor](https://img.shields.io/badge/sponsor-donate-pink?logo=github-sponsors\u0026logoColor=white)](https://github.com/sponsors/hbakri)\n[![Buy me a coffee](https://img.shields.io/badge/buy_me_a_coffee-donate-pink?logo=buy-me-a-coffee\u0026logoColor=white)](https://www.buymeacoffee.com/hbakri)\n\nYour kindness and support make a world of difference. Thank you! 🙏\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhbakri%2Fdjango-rest-testing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhbakri%2Fdjango-rest-testing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhbakri%2Fdjango-rest-testing/lists"}