Django アプリを作ると自動で生成される tests.py。
ほとんどの開発者は一度はこんなことを考えます。
「このファイル… 消してもいいんじゃないの?」
「一体どう使えばいいの?」
実際、 tests.py は Django が公式に推奨するテストの入り口 であり、
少し慣れるとリファクタリングや機能追加、バージョンアップ時に 本当に大きな保険 となります。 (Django Project)
この記事では:
-
tests.pyが何のためのファイルなのか -
どのように書いて実行するのか
-
どのような状況で特に有用なのか
-
実際の使用例
を一度に整理します。
1. tests.py はどんなファイルか?
Django は Python 標準ライブラリの unittest に基づくテストフレームワーク を使用します。
-
django.test.TestCaseなどを継承したテストクラスを作成し -
その中に
test_で始まるメソッドを書けば -
./manage.py testコマンドで自動的に探して実行します。
startapp でアプリを作成すると自動的に tests.py が生成されますが:
-
小さなプロジェクト →
tests.py一つでも十分 -
規模が大きくなると →
tests/パッケージに分けること(test_models.py、test_views.pyなど)を Django もドキュメントで推奨しています。
つまり、 「アプリ単位のテストのための基本的な位置」 と理解すれば良いでしょう。
2. いつ有用なのか? (なぜわざわざテストをするのか)
Django のテストは特に次のような状況で光ります。
-
モデル/ビジネスロジックの検証
-
計算ロジック、状態変更、カスタムメソッドが複雑であればあるほど
-
一度テストを作成しておけば、以降の修正時にも安心してリファクタリングが可能です。
-
-
ビュー/URL/権限チェック
-
認証の有無に応じて応答コードが異なるか
-
誤った入力に対して適切にリダイレクト/エラー処理できるか
-
テンプレートに必要なコンテキストが正しく渡されるか
-
-
リグレッションバグを防ぐ
- バグを一度捕まえたら、 テストで固定 しておけば同じ問題が再発しない
-
バージョンアップ時の安定性確保
-
Django またはライブラリのバージョンを上げるとき
-
./manage.py testを一度回してから始められます。
-
3. 最も基本的な tests.py の例
3-1. モデルテスト
# myapp/tests.py
from django.test import TestCase
from .models import Article
class ArticleModelTests(TestCase):
def setUp(self):
self.article = Article.objects.create(
title="テストタイトル",
content="本文",
views=0,
)
def test_increase_views(self):
# given
self.assertEqual(self.article.views, 0)
# when
self.article.increase_views()
# then
self.assertEqual(self.article.views, 1)
-
TestCaseはunittest.TestCaseを拡張した Django 用のクラスです。 -
各テストは トランザクション単位で隔離 され、テストごとに DB がロールバックされて互いに干渉しません。
4. ビュー/URL をテストする方法 - Client の使用
Django は テスト専用 HTTP クライアント django.test.Client を提供しています。
実際のサーバーを起動せずに URL に GET/POST リクエストを送り、応答を検査できます。
4-1. 簡単なビューテスト
# myapp/tests.py
from django.test import TestCase
from django.urls import reverse
from .models import Article
class ArticleViewTests(TestCase):
def setUp(self):
self.article = Article.objects.create(
title="記事 1",
content="内容",
views=0,
)
def test_article_detail_page_returns_200(self):
url = reverse("article_detail", args=[self.article.id])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "記事 1")
ここでのポイントは:
-
self.client:TestCaseに基本的に提供されるテストクライアントインスタンス -
reverse("article_detail", args=[...]): URL ハードコーディングの代わりに URL 名で取得 -
assertContains: 応答本文に特定の文字列が含まれているかを検査
4-2. ログイン/権限チェックテスト
from django.contrib.auth import get_user_model
class ArticlePermissionTests(TestCase):
def setUp(self):
self.user = get_user_model().objects.create_user(
username="tester",
password="pass1234",
)
self.article = Article.objects.create(
title="秘密",
content="秘密の投稿",
views=0,
)
def test_anonymous_user_redirected_to_login(self):
url = reverse("article_edit", args=[self.article.id])
response = self.client.get(url)
self.assertEqual(response.status_code, 302)
self.assertIn("/accounts/login", response["Location"])
def test_logged_in_user_can_access_edit_page(self):
self.client.login(username="tester", password="pass1234")
url = reverse("article_edit", args=[self.article.id])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
このように:
-
匿名ユーザーがアクセスした際にリダイレクトされるか
-
ログインしたユーザーは正常にアクセスできるか
権限関連のバグを初期に見つけることができます。
5. tests.py はいつ / どのように実行するのか?
5-1. 全体テストを実行する
$ python manage.py test
- 現在のディレクトリ以下の
test*.pyファイルから
unittest.TestCaseまたはdjango.test.TestCaseのサブクラスを自動的に探して実行します。
5-2. 特定のアプリのみ実行
$ python manage.py test myapp
5-3. 特定のモジュール / クラス / メソッドのみ実行
# myapp.tests モジュール全体
$ python manage.py test myapp.tests
# 特定の TestCase
$ python manage.py test myapp.tests.ArticleViewTests
# 特定のテストメソッド
$ python manage.py test myapp.tests.ArticleViewTests.test_article_detail_page_returns_200
開発中は 修正した部分だけを迅速に回してみる習慣 をつけると良いでしょう。
6. tests.py を tests パッケージに拡張する
プロジェクトが大きくなると tests.py 一つでは対応できなくなります。
Django のドキュメントでも パッケージ構造に分けることを推奨 しています。(Django Project)
例:
myapp/
tests/
__init__.py
test_models.py
test_views.py
test_forms.py
test_api.py
各ファイルの中では:
# myapp/tests/test_models.py
from django.test import TestCase
from myapp.models import Article
class ArticleModelTests(TestCase):
...
このように分けても実行は同様です。
$ python manage.py test myapp # 全体
$ python manage.py test myapp.tests.test_models # モデルテストのみ
7. 実践使用例いくつか
7-1. “バグ再現用テスト” でリグレッション防止
-
プロダクションでバグが発生
-
ローカルで再現用テストを作成(失敗するのが正常)
-
コードを修正
-
テストが成功するか確認
-
gitに テスト + 修正コードを一緒にコミット
こうしておけば、後でリファクタリングする際に同じバグが再発しません。
7-2. API 応答フォーマットを固定する
フロントエンドとの協力時、API 応答形式が非常に重要です。
class ArticleApiTests(TestCase):
def setUp(self):
self.article = Article.objects.create(
title="API タイトル",
content="内容",
views=0,
)
def test_article_detail_api_response(self):
url = reverse("api:article-detail", args=[self.article.id])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertEqual(data["id"], self.article.id)
self.assertIn("title", data)
self.assertIn("content", data)
-
応答にどのフィールドが必ず含まれるべきかを明示する
-
フィールド名を誤って変更または削除するとテストですぐに失敗する
7-3. フォーム検証ロジックのテスト
from .forms import ArticleForm
class ArticleFormTests(TestCase):
def test_title_is_required(self):
form = ArticleForm(data={"title": "", "content": "内容"})
self.assertFalse(form.is_valid())
self.assertIn("title", form.errors)
def test_valid_form(self):
form = ArticleForm(data={"title": "タイトル", "content": "内容"})
self.assertTrue(form.is_valid())
- フォームの有効性検査ルールをドキュメントのようにテストで残すことができます。

8. まとめ - tests.py を消さずに、一度正しく使ってみよう
一言でまとめると:
tests.pyは「Django アプリの安全ネット」を作成するファイルです。
-
最初は面倒ですが、
-
一度 テストを基に開発する流れ を味わうと
-
リファクタリング/機能追加/バージョンアップ時のストレスが大幅に減少します。
実際のプロジェクトでは:
-
新機能を作る時 - 最低1~2個のテストを追加する
-
バグを修正する時 - 必ずそのバグを再現するテストを最初に作成する
-
プロジェクトを拡張し始める時 -
tests.py→tests/パッケージにリファクタリング
このパターンさえ守れば、「テストのない Django プロジェクト」とは全く異なる開発体験が得られるでしょう。
コメントはありません。