Django アプリを作ると自動で生成される tests.py
ほとんどの開発者は一度はこんなことを考えます。

「このファイル… 消してもいいんじゃないの?」
「一体どう使えばいいの?」

実際、 tests.pyDjango が公式に推奨するテストの入り口 であり、
少し慣れるとリファクタリングや機能追加、バージョンアップ時に 本当に大きな保険 となります。 (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.pytest_views.py など)を Django もドキュメントで推奨しています。

つまり、 「アプリ単位のテストのための基本的な位置」 と理解すれば良いでしょう。


2. いつ有用なのか? (なぜわざわざテストをするのか)

Django のテストは特に次のような状況で光ります。

  1. モデル/ビジネスロジックの検証

    • 計算ロジック、状態変更、カスタムメソッドが複雑であればあるほど

    • 一度テストを作成しておけば、以降の修正時にも安心してリファクタリングが可能です。

  2. ビュー/URL/権限チェック

    • 認証の有無に応じて応答コードが異なるか

    • 誤った入力に対して適切にリダイレクト/エラー処理できるか

    • テンプレートに必要なコンテキストが正しく渡されるか

  3. リグレッションバグを防ぐ

    • バグを一度捕まえたら、 テストで固定 しておけば同じ問題が再発しない
  4. バージョンアップ時の安定性確保

    • 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)
  • TestCaseunittest.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. “バグ再現用テスト” でリグレッション防止

  1. プロダクションでバグが発生

  2. ローカルで再現用テストを作成(失敗するのが正常)

  3. コードを修正

  4. テストが成功するか確認

  5. 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())
  • フォームの有効性検査ルールをドキュメントのようにテストで残すことができます。

test pyを作成する Django dev

8. まとめ - tests.py を消さずに、一度正しく使ってみよう

一言でまとめると:

tests.py は「Django アプリの安全ネット」を作成するファイルです。

  • 最初は面倒ですが、

  • 一度 テストを基に開発する流れ を味わうと

  • リファクタリング/機能追加/バージョンアップ時のストレスが大幅に減少します。

実際のプロジェクトでは:

  1. 新機能を作る時 - 最低1~2個のテストを追加する

  2. バグを修正する時 - 必ずそのバグを再現するテストを最初に作成する

  3. プロジェクトを拡張し始める時 - tests.pytests/ パッケージにリファクタリング

このパターンさえ守れば、「テストのない Django プロジェクト」とは全く異なる開発体験が得られるでしょう。