Django REST Framework API テスト方法

INEZA Felin-Michel

INEZA Felin-Michel

22 5月 2026

Django REST Framework API テスト方法

Apidog エンタープライズ

オンプレミスデプロイ

SSO & RBAC

SOC 2 準拠

Apidog Enterpriseを見る

通常DRFと略されるDjango REST Frameworkは、Django上にAPIを構築するための標準的なツールキットです。これにはシリアライザー、ビューセット、ルーター、認証レイヤーが含まれています。また、多くの開発者があまり活用していないものとして、Django独自のテストランナーを基盤とした堅牢なテストレイヤーも提供されています。

このガイドでは、DRF APIを2つの方法でテストする方法を紹介します。1つ目は、DRFのAPITestCaseAPIClientを使用してPythonで記述された自動テストです。これらはライブサーバーなしで実行され、すべてのコミットでリグレッションを検出します。2つ目は、APIクライアントで実行中のエンドポイントを実際に操作する方法です。これは、動作を探り、実際のサービスを検証する方法です。どちらも重要であり、異なる問題を発見します。

プロジェクトとテスト環境のセットアップ

テストの依存関係がシステムの他の部分から分離されるように、隔離された環境から始めます。

python -m venv venv
source venv/bin/activate
pip install django djangorestframework coverage

DRFプロジェクトのテストは、他のDjangoプロジェクトと同様に行われます。慣例として、テストは各アプリ内のtests.pyファイル、または多数ある場合はtests/パッケージに配置されます。Djangoのテストランナーは、TestCaseの派生クラスとtest_で始まる名前のメソッドをすべて検出します。

重要な選択は、どの基底クラスを使用するかです。通常のunittest.TestCaseにはデータベースがありません。DjangoのTestCaseは各テストをトランザクションで囲み、ロールバックするため、テストが互いに汚染されることはありません。DRFのAPITestCaseはDjangoのTestCaseを拡張し、DRFの認証とコンテンツタイプを理解するDRFのAPIClientに置き換えます。APIの作業にはAPITestCaseを使用します。

もう1つ知っておくべきクラスがあります。TransactionTestCaseはテストをトランザクションで囲まないため、テスト対象のコードがトランザクション自体を管理している場合や、トランザクションで囲むと隠されてしまうデータベース機能に依存している場合に必要となります。ロールバックする代わりにテスト間でテーブルを切り捨てるため、速度が遅くなります。そのため、TestCaseがその動作を本当にモデル化できない場合にのみ使用してください。ほとんどのエンドポイントテストでは、APITestCaseが適切で最速の選択肢です。

テストデータを早期に検討することも役立ちます。クラス内の各テストが必要とする行を作成するにはsetUpメソッドを使用するか、データが読み取り専用で速度のためにクラス全体で共有できる場合はsetUpTestDataを使用します。大規模なプロジェクトでは、factory_boyのようなファクトリライブラリを使用すると、すべてのフィールドを明示的に指定することなく有効なモデルインスタンスを生成できるため、モデルに新しい必須フィールドが追加された場合でもテストを短く、堅牢に保つことができます。

シリアライザーをユニットとしてテストする

シリアライザーはバリデーションを行い、モデルインスタンスとJSONの間で変換します。これらは小さく純粋であるため、エンドポイントに触れない高速なユニットテストに理想的です。

ArticleモデルとArticleSerializerがあるとします。ユニットテストは、有効なデータがパスし、無効なデータが失敗することを確認します。

from django.test import TestCase
from articles.serializers import ArticleSerializer


class ArticleSerializerTests(TestCase):

    def test_valid_data_passes(self):
        data = {"title": "Caching strategies", "body": "Use ETags."}
        serializer = ArticleSerializer(data=data)
        self.assertTrue(serializer.is_valid())

    def test_missing_title_fails(self):
        data = {"body": "No title here."}
        serializer = ArticleSerializer(data=data)
        self.assertFalse(serializer.is_valid())
        self.assertIn("title", serializer.errors)

これらのテストはHTTPやビューがないため、ミリ秒単位で実行されます。エンドポイントを検討する前に、バリデーションルールが正しいことを確認できます。このユニットレベルのカバレッジは、自動テストとは何かで説明されているテストピラミッドの基盤となります。

APITestCaseとAPIClientでエンドポイントをテストする

エンドポイントテストは、ルーティング、ビュー、シリアライザー、レスポンスといった完全なパスをチェックします。DRFのAPIClientはリクエストをプロセス内で送信するため、サーバーが実行されることなくテストは高速に保たれます。

from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from articles.models import Article


class ArticleEndpointTests(APITestCase):

    def setUp(self):
        Article.objects.create(title="First post", body="Hello world")

    def test_list_articles_returns_200(self):
        url = reverse("article-list")
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data), 1)

    def test_create_article(self):
        url = reverse("article-list")
        payload = {"title": "Second post", "body": "More content"}
        response = self.client.post(url, payload, format="json")
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(Article.objects.count(), 2)

いくつか注目すべき詳細があります。self.clientAPITestCaseによって提供されるAPIClientインスタンスです。URLをハードコードする代わりに、ルート名でreverse()を使用することで、ルート変更によってすべてのテストが壊れることを防ぎます。書き込み時にはformat="json"を渡して、クライアントがペイロードを正しくシリアライズするようにします。ステータスコードは、生の数値よりも明確に読み取れるため、rest_framework.statusの名前付き定数でアサートします。ステータスコード自体は、REST APIが使用すべきHTTPステータスコードに関するガイドでカバーされています。

認証とパーミッションのテスト

ほとんどの実際のAPIはエンドポイントを保護しています。認証されていないリクエストが拒否され、適切なパーミッションを持つ認証済みリクエストが成功することを証明するテストが必要です。

APIClientにはテストを認証する2つの方法があります。force_authenticate()は認証情報のチェックをスキップし、ユーザーを直接アタッチするため、ビューロジック自体をテストするのに理想的です。login()またはcredentials()は実際の認証パスを実行します。両方向を使用したパーミッションテストの例を以下に示します。

from django.contrib.auth.models import User
from rest_framework import status
from rest_framework.test import APITestCase
from django.urls import reverse


class ArticlePermissionTests(APITestCase):

    def setUp(self):
        self.user = User.objects.create_user(
            username="editor", password="testpass123"
        )
        self.url = reverse("article-list")

    def test_anonymous_cannot_create(self):
        payload = {"title": "Blocked", "body": "Should fail"}
        response = self.client.post(self.url, payload, format="json")
        self.assertEqual(
            response.status_code, status.HTTP_403_FORBIDDEN
        )

    def test_authenticated_user_can_create(self):
        self.client.force_authenticate(user=self.user)
        payload = {"title": "Allowed", "body": "Should pass"}
        response = self.client.post(self.url, payload, format="json")
        self.assertEqual(
            response.status_code, status.HTTP_201_CREATED
        )

最初のテストは、パーミッションクラスが匿名での書き込みをブロックすることを確認します。2番目のテストは、認証されたユーザーが通過することを確認します。これらを合わせることで、パーミッションの境界を明確にし、リファクタリング中に静かに壊れてしまうような振る舞いを特定します。特にトークン認証をテストしている場合は、実際のヘッダーパスをテストするためにforce_authenticateself.client.credentials(HTTP_AUTHORIZATION="Token " + token)に置き換えてください。

どちらを使用するかは意図的に決定する価値があります。force_authenticateは高速でビューロジックを分離するため、特定のロールができること・できないことだけを気にするような大部分のパーミッションテストに適しています。実際の認証情報パスは、認証メカニズムが機能すること、つまり不正なトークンが拒否されること、期限切れのトークンが失敗すること、ログインエンドポイントが利用可能なトークンを発行することなどを具体的に証明する、より少数のテストに必要です。両方を組み合わせることで、広範なカバレッジを低コストで得られ、重要な部分では深いカバレッジを実現できます。

オブジェクトレベルのパーミッションも忘れないでください。多くのDRF APIでは、ユーザーは自分のレコードを編集できますが、他のユーザーのレコードは編集できません。これを明示的にテストしてください。2人のユーザーを作成し、一方がレコードを作成した後、もう一方のユーザーがそれを変更しようとしたときに403または404を受け取ることをアサートします。詳細エンドポイントがロックダウンされていても、漏洩したクエリセットがユーザーが絶対に見るべきではない行を返す可能性があるため、リストエンドポイントも同様に精査する必要があります。

テストスイートの実行とカバレッジの測定

Djangoのテストランナーですべてのテストを実行します。

python manage.py test

ランナーはテストクラスを検出し、一時的なテストデータベースをセットアップし、各テストをトランザクション内で実行し、その後ロールバックします。クリーンな実行ではOKが出力されます。失敗した場合は、壊れたアサーションとその場所が表示されます。

スイートがパスすることを知っていることは、十分にカバーされていることを知っていることとは異なります。coverageツールは、実際にどの行が実行されたかを測定します。

coverage run --source='.' manage.py test
coverage report
coverage html

レポートには、各ファイルと実行された行の割合がリストされます。HTML出力では、テストされていない行が赤色でハイライト表示され、不足している箇所がすぐにわかります。単一の数字を目標にするのではなく、ビュー、シリアライザー、パーミッションの意味のあるカバレッジを目指してください。自動テストスクリプトの書き方に関する記事では、弱いテストのカバレッジは本当の安全性ではないため、保持する価値のあるテストとは何かについて解説しています。

クライアントでライブAPIをテストする

Pythonの自動テストは高速でCIで実行されますが、アプリをプロセス内でテストします。これらは、実行中のサービスに対してのみ現れる問題、例えばCORSヘッダーの誤設定、リバースプロキシによる何らかの情報の削除、実際の負荷がかかったときのデータベースの遅延、テストデータベースと本番環境の差異などを捕捉しません。そのためには、デプロイされたエンドポイントに実際のリクエストを送信します。

ここではApidogが非常に適しています。これはオールインワンのAPIプラットフォームであり、DRF APIのOpenAPIスキーマをインポートし、実行中のサーバーにライブリクエストを送信し、追加のPythonコードを書かずに視覚的にアサーションを構築できます。DRFはOpenAPIスキーマを生成でき、Apidogはそれを直接利用するため、クライアントと実際の契約が同期された状態に保たれます。また、ログイン、記事の作成、取得、削除などの多段階テストシナリオを構築し、それらをスケジュールまたはCIで実行することも可能です。これはAPITestCaseスイートを補完するものであり、置き換えるものではありません。ユニットテストとエンドポイントテストはコードを保護し、APIクライアントはデプロイされたサービスを保護します。ApidogをダウンロードしてDRFスキーマをインポートし、試すことができます。Pythonでエンドツーエンドを完結させたいチーム向けには、pytest API自動テストフレームワークガイドが、pytestでDRFスタイルのテストを実行する方法を示しています。

よくある質問

TestCaseとAPITestCaseの違いは何ですか?

DjangoのTestCaseは、各テストをデータベーストランザクションで囲み、標準のDjangoテストクライアントを提供します。DRFのAPITestCaseはそれをサブクラス化し、DRFの認証スキーム、コンテンツネゴシエーション、リクエストのformat引数を理解するAPIClientにクライアントを置き換えます。DRFのエンドポイントをテストするにはAPITestCaseを使用します。

loginの代わりにforce_authenticateを使用すべきなのはいつですか?

実際の認証フローのコストと複雑さなしにビューとパーミッションロジックをテストしたい場合は、force_authenticate()を使用します。これはユーザーをリクエストに直接アタッチします。セッション認証やトークン認証など、認証メカニズム自体を検証したい場合は、login()またはcredentials()を使用します。

DRFテストには実行中のサーバーが必要ですか?

いいえ。APIClientはリクエストをプロセス内で直接ビューにディスパッチするため、テストスイートはサーバーを起動せずに実行されます。これが高速である理由です。プロキシやCORSなどのインフラストラクチャを含む実際にデプロイされたサービスをテストするには、ApidogのようなAPIクライアントを使用して実際のHTTPリクエストを送信します。

DRFプロジェクトのテストカバレッジを確認するにはどうすればよいですか?

coverageパッケージをインストールし、coverage run --source='.' manage.py testを実行した後、概要を表示するにはcoverage report、行ごとの表示にはcoverage htmlを実行します。HTMLレポートはテストされていない行を強調表示するため、どのビューやシリアライザーにテストが不足しているかを正確に確認できます。

シリアライザーとエンドポイントは個別にテストすべきですか?

はい。シリアライザーのユニットテストは高速で、HTTPオーバーヘッドなしでバリデーションのバグを特定します。APITestCaseを使用したエンドポイントテストは、ルーティング、パーミッション、および完全なリクエストサイクルをチェックします。両方を維持することで、ユニットレベルでの迅速なフィードバックと、統合レベルで接続が機能しているという確信が得られます。

ApidogでAPIデザイン中心のアプローチを取る

APIの開発と利用をよりシンプルなことにする方法を発見できる