Django Advent Calendar 2023 の12日目の記事です。
前日は @ryu22e さんの Django 5.0 主な変更点まとめ #Python - Qiita でした。
問題意識
普段から Django と Django REST Framework (DRF) を使っているのですが、テストケースを書く際、いろんな TestCase
クラスの選択肢があり、いつもその場のノリで選んでしまっています。
この場を借りてどのクラスを使うのがベストなのか考えます。
// 動作確認に利用したパッケージ Django 4.1.2 djangorestframework 3.14.0
いろんな TestCase
Django もしくは DRF でクラスベースのテストを書く際の選択肢は以下です
- Python の標準ライブラリである unittest.TestCase
- Django の django.test.TestCase
- DRF の rest_framework.test.APITestCase
下の2つは Python 標準ライブラリを継承したクラスであることは予想できます。 実際にどう違うのか調べます。
unittest.TestCase
Django に限らず、 Python のユニットテストで利用できるクラスです。
self.assertHogehoge
というアサートメソッドを持っています。
django.test.TestCase
unittest.TestCase
を継承したDjango のテストケースクラスですが、実は直接継承しているわけではありません。
つまり、 Django の TestCase
は SimpleTestCase
と TransactionTestCase
を継承したクラスです。
ドキュメント ではこう記述しています。
あなたの Django アプリケーションがデータベースを使用しない場合は、SimpleTestCase を使ってください。
特定のデータベーストランザクションの振る舞いをテストしたい場合は、TransactionTestCase を使ってください。
データベースを使い、かつトランザクション関連のテストが必要ない場合は django.test.TestCase
を使うのがよさそうです。
unittest.TestCase or SimpleTestCase
データベースを使わないテストは SimpleTestCase とあるが、unittest.TestCase でいいのでは?と思ったので差分を確認します。
アサーションは特殊な関数が多いので、 Client クラスが Django のテストクラスにとって重要な存在と言えそうです。
Viewクラスに対する簡単なテストケースを書いてみました。
unittest.TestCase だと初期化関数 (setUp
) で self.client
の設定が必要ですが、 SimpleTestCase では不要です。
import unittest from django.test import SimpleTestCase from django.test.client import Client # python 標準 class TestConvertViewUnittest(unittest.TestCase): def setUp(self): self.client = Client() def test_get_ok(self): response = self.client.get("/api/converter/", data={"text": "test"}) self.assertEqual(response.status_code, 200) # Django class TestConvertViewSimple(SimpleTestCase): def test_get_ok(self): response = self.client.get("/api/converter/", data={"text": "test"}) self.assertEqual(response.status_code, 200) # SimpleTestCase のアサーション # status と レスポンスのテキストを同時にアサート self.assertContains(response, text="ティーイーエスティー", status_code=200) # response.json() とすれば assertDict で代替可能 self.assertJSONEqual(response.content, {"text": "ティーイーエスティー"})
rest_framework.test.APITestCase
DRF のテストクラスは Django のテストクラスと対応しています。
- SimpleTestCase -> APISimpleTestCase
- TransactionTestCase -> APITransactionTestCase
- TestCase -> APITestCase
Django のテストクラスとの差は主に client に APIClient
が設定されている部分です。
REST framework includes the following test case classes, that mirror the existing Django's test case classes, but use APIClient instead of Django's default Client. 該当コード (GitHub)
APIClient
APIClient は基本的には Django の Client を継承したクラスです。
django.SimpleTestCase の場合は データベースを利用しないため特に影響はありません。
class TestConvertViewDRF(APISimpleTestCase): def test_get_ok(self): response = self.client.get("/api/converter/", data={"text": "test"}) self.assertEqual(response.status_code, 200) self.assertContains(response, text="ティーイーエスティー", status_code=200) self.assertJSONEqual(response.content, {"text": "ティーイーエスティー"})
データベースを利用し、 User モデル による認証が必要な場合、 APIClient
が役に立ちます。
force_authenticate
でリクエストを強制的に実行できるからです。
以下のように authentication_classes
と permission_classes
が設定されているViewクラスを例にテストクラスを考えます。
# view.py from rest_framework.generics import RetrieveAPIView from rest_framework.authentication import BasicAuthentication, SessionAuthentication from rest_framework.permissions import IsAuthenticated class AlphabetView(RetrieveAPIView): serializer_class = AlphabetRequestSerializer authentication_classes = [BasicAuthentication, SessionAuthentication] permission_classes = [IsAuthenticated]
データベースを利用するため Simple なしのクラス APITestCase を使います。
認証用のユーザーを作成し、
リクエストを実行前に force_authenticate
で作成したユーザーを渡すことで認証が可能になります。
from django.contrib.auth import get_user_model from rest_framework.test import APITestCase class TestAlphabetViewDRF(APITestCase): def setUp(self): # get_user_model は django.contrib.auth.models.User と同じ self.user = get_user_model().objects.create_user(username="test_user") def test_get_ok(self): self.client.force_authenticate(user=self.user) response = self.client.get("/api/alphabet/") self.assertEqual(response.status_code, 200) def test_get_ng(self): # 認証なし response = self.client.get("/api/alphabet/") self.assertEqual(response.status_code, 401)
まとめ
- Django のテストケースクラスは
self.client
を使ってリクエストのテストができる- データベースを使わないときは
SimpleTestCase
- 使う場合は
TestCase
- データベースを使わないときは
- DRF のテストケースクラスは Django のとほぼ同じ
- 認証が必要な場合の設定は
APITestCase
を使うと楽 (かもしれない)
- 認証が必要な場合の設定は