當你創建 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 類。 -
每個測試都是以 事務單元隔離的,每個測試後資料庫均會回滾,互不干擾。
4. 如何測試視圖/URL – 使用 Client
Django 提供了專用的測試 HTTP 客戶端 django.test.Client。
不啟動實際伺服器的情況下,也能發送 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 項目”完全不同的開發體驗。
目前沒有評論。