やりたいこと
django の Web API のレスポンスが期待通りかを自動でテストする。
テストには pytest を使用する。
django の Web API 実装例
ここでは以下の Item の生成 (create)、一覧 (list) などの操作を行える API を用意する。
app1/models.py
class Item(models.Model):
name = models.CharField(max_length=32)
is_deleted = models.BooleanField(default=False)
ファイル構成
pytest1
├── README.txt
├── app1
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ...
│ ├── services.py
│ └── views.py
├── manage.py
├── pytest1
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── schema.yml
API 例
- POST /app1/create/?name={name}
- GET /app1/list/
API 実装例
app1/views.py
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from rest_framework import status
from app1.services import (
create,
list,
)
@csrf_exempt
@require_http_methods(["POST"])
def api_create(request):
response = {
'data': None,
}
name = request.GET.get('name')
item = create(name)
response['data'] = to_json(item)
return JsonResponse(response)
@csrf_exempt
@require_http_methods(["GET"])
def api_list(request):
response = {
'data': None,
}
items = list()
response['data'] = to_json_list(items)
return JsonResponse(response)
app1/services.py
app1/services.py
from app1.models import Item
def create(name):
item = Item(
name=name,
)
item.save()
return item
def list():
items = Item.objects.filter(
is_deleted=False
).order_by("id")
return items
pytest
パッケージのインストール
$ pip install pytest
$ pip install pytest-django
テスト用のファイル構成
pytest1
├── app1
│ └── test_apis
│ ├── __init__.py
│ ├── test_api_create.py
│ └── test_api_list.py
├── conftest.py
└── pytest.ini
conftest.py
conftest.py には複数のテストで使う共通的な処理を定義する。
@pytest.fixture デコレータを付与することで、テストメソッドで使用することができる。
conftest.py
import pytest
from rest_framework.test import APIClient
@pytest.fixture
def api_client():
client = APIClient()
return client
pytest.ini
pytest のカスタマイズのための設定ファイル。
pytest.ini
[pytest]
env_override_existing_values=1
env_files=
.env
DJANGO_SETTINGS_MODULE=pytest1.settings
テストプログラム
ここでは app1/views.py に定義した API に対して、app1/test_apis/ ディレクトリに API 毎にテストファイルを作成している。
- app1/views.py: API の定義
- app1/test_api_create.py: /app1/create/ のテスト
- app1/test_api_list.py: /app1/list/ のテスト
test_api_create.py
app1/test_apis/test_api_create.py
import pytest
from rest_framework import status
from rest_framework.response import Response
from rest_framework.test import APIClient
from app1.models import Item
class TestApiCreate:
@pytest.mark.django_db
def test_basic(self, api_client):
api_client.raise_request_exception = True
# api_client.force_authenticate(user=test_user)
# request
name = 'item_01'
url = f'/app1/create/?name={name}'
response = api_client.post(url)
# レスポンスのステータスコードを確認
assert response.status_code == status.HTTP_201_CREATED
#
response_obj = response.json()
print(response_obj)
assert response_obj['data']['name'] == name
# database
item = Item.objects.get(id=response_obj['data']['id'])
assert item
assert response_obj['data']['name'] == item.name
test_api_list.py
app1/test_apis/test_api_list.py
import pytest
from rest_framework import status
from rest_framework.response import Response
from rest_framework.test import APIClient
from app1.models import Item
class TestApiCreate:
@pytest.mark.django_db
def test_basic(self, api_client):
api_client.raise_request_exception = True
# Item オブジェクトを作成
items = []
for i in range(1, 4):
name = f"item_{i:02d}"
item = Item.objects.create(
name=name,
is_deleted=False,
)
items.append(item)
# API にリクエストを送信
url = f'/app1/list/'
response = api_client.get(url)
# レスポンスの HTTP ステータスコードを確認
assert response.status_code == status.HTTP_200_OK
# レスポンスのデータを確認
response_obj = response.json()
print(response_obj)
for item in items:
obj = next(
(obj for obj in response_obj['data'] if obj['id'] == item.id),
None
)
# id が一致するオブジェクトが取得できていることを確認
assert obj
# 該当のオブジェクトの name が一致することを確認
assert obj['name'] == item.name
pytest 実行例
pytest の実行方法
テストファイルが格納されているディレクトリまたはファイル名を指定して pytest を実行することができる。
ディレクトリ指定の場合
$ python app1/test_apis
ファイル指定の場合
$ python app1/test_apis/test_api_create.py
pytest 実行結果例
$ pytest app1/test_apis
============================= test session starts ==============================
platform linux -- Python 3.12.5, pytest-9.0.2, pluggy-1.6.0
django: version: 5.1.6, settings: pytest1.settings (from ini)
rootdir: .../pytest1
configfile: pytest.ini
plugins: dotenv-0.5.2, django-4.11.1
collected 2 items
app1/test_apis/test_api_create.py . [ 50%]
app1/test_apis/test_api_list.py . [100%]
============================== 2 passed in 0.93s ===============================
最下行に 2 passed が出力されており、2つのテストが成功したことがわかる。
-s を指定すると stdout の出力内容を確認することができる。
$ pytest app1/test_apis -s
============================= test session starts ==============================
platform linux -- Python 3.12.5, pytest-9.0.2, pluggy-1.6.0
django: version: 5.1.6, settings: pytest1.settings (from ini)
rootdir: .../pytest1
configfile: pytest.ini
plugins: dotenv-0.5.2, django-4.11.1
collected 2 items
app1/test_apis/test_api_create.py {'data': {'id': 1, 'name': 'item_01', 'is_deleted': False}}
.
app1/test_apis/test_api_list.py {'data': [{'id': 2, 'name': 'item_01', 'is_deleted': False}, {'id': 3, 'name': 'item_02', 'is_deleted': False}, {'id': 4, 'name': 'item_03', 'is_deleted': False}]}
.
============================== 2 passed in 0.86s ===============================