View Django chạy đúng trên máy bạn chưa chắc đã chạy đúng trên production — chỉ một slug sai hay một trường form thiếu cũng đủ biến trang thành lỗi 500. Viết test bằng TestCase và Client có sẵn của Django giúp bạn gọi view như trình duyệt thật, kiểm tra status code, nội dung và template chỉ trong vài dòng, rồi chạy lại toàn bộ chỉ với một lệnh python manage.py test trước mỗi lần deploy.

Vấn đề

Nhiều dự án Django nhỏ không có test nào cho view. Quy trình kiểm tra duy nhất là mở trình duyệt, bấm qua vài trang, thấy chạy thì deploy. Cách này bỏ sót các trường hợp biên: slug không tồn tại, form thiếu trường, trang yêu cầu đăng nhập. Ví dụ view chi tiết bài viết dùng Post.objects.get() trực tiếp:

# views.py
from django.shortcuts import render
from .models import Post
def post_detail(request, slug):
post = Post.objects.get(slug=slug) # slug sai -> DoesNotExist -> lỗi 500
return render(request, "blog/detail.html", {"post": post})

Người dùng chỉ cần gõ nhầm một ký tự trong URL là nhận ngay trang lỗi 500 thay vì 404, và bạn chỉ biết khi có người báo lại. Lỗi càng nằm sâu trong các trường hợp hiếm gặp thì càng tốn thời gian sửa.

Giải pháp

Django đã có sẵn django.test.TestCase và test Client — không cần cài thêm thư viện nào. TestCase tạo database test riêng và tự dọn sạch sau mỗi test; Client gửi request thật qua URL như trình duyệt, trả về response để bạn assert status code, nội dung và template. Viết test cho view trên như sau:

# tests.py
from django.test import TestCase
from django.urls import reverse
from .models import Post
class PostDetailViewTests(TestCase):
@classmethod
def setUpTestData(cls):
cls.post = Post.objects.create(
title="Hello Django", slug="hello-django",
content="Nội dung bài viết.",
)
def test_detail_tra_200_va_hien_tieu_de(self):
response = self.client.get(
reverse("post-detail", kwargs={"slug": "hello-django"})
)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Hello Django")
self.assertTemplateUsed(response, "blog/detail.html")
def test_slug_sai_tra_404(self):
response = self.client.get(
reverse("post-detail", kwargs={"slug": "khong-ton-tai"})
)
self.assertEqual(response.status_code, 404)

Chạy python manage.py test — test thứ hai sẽ FAIL ngay, chỉ ra view đang trả 500 thay vì 404. Sửa view bằng get_object_or_404 là xong:

# views.py — bản sửa
from django.shortcuts import get_object_or_404, render
from .models import Post
def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug)
return render(request, "blog/detail.html", {"post": post})

Chạy lại test, cả hai đều pass. setUpTestData chạy một lần cho cả class nên test nhanh; mỗi test vẫn chạy trong transaction riêng nên dữ liệu không rò rỉ sang test khác. Khi view thay đổi, chạy lại toàn bộ test là biết ngay chỗ nào vỡ.

Áp dụng vào dự án của bạn

Bắt đầu từ ba view quan trọng nhất: trang chi tiết hay gặp lỗi nhất, form tạo và sửa dữ liệu, và trang yêu cầu đăng nhập. Mỗi view chỉ cần hai đến ba test: trường hợp đúng trả 200 và hiện nội dung, trường hợp sai trả 404 hoặc redirect, và trường hợp form lỗi trả lại đúng form kèm thông báo. Gắn python manage.py test vào bước cuối trước khi deploy — hoặc vào CI — để lỗi mới không bao giờ lọt ra production mà không ai hay.

Bài viết liên quan

Câu hỏi thường gặp

TestCase khác gì SimpleTestCase?

SimpleTestCase không cần database, dùng cho test không chạm DB như form hay template tag. TestCase tạo database test riêng và bọc mỗi test trong một transaction nên phù hợp khi view truy vấn model.

Client trong test khác gì requests?

self.client là test Client của Django: nó đi qua URLconf, middleware và view thật nhưng không cần chạy server, không cần mạng. Nó trả về response có sẵn status_code, content, context và templates để bạn assert trực tiếp.

Test có làm chậm deploy không?

Một bộ test view nhỏ thường chạy dưới 10 giây. Chạy python manage.py test trước khi deploy rẻ hơn nhiều so với việc sửa lỗi 500 trên production lúc nửa đêm.

Nên đặt test ở đâu?

Đặt trong tests.py của từng app, hoặc thư mục tests/ nếu app lớn. Django tự phát hiện file bắt đầu bằng test khi bạn chạy python manage.py test.

💬 Bạn đã thử cách này trong dự án của mình chưa? Nếu gặp lỗi hoặc có cách làm gọn hơn, để lại bình luận bên dưới — mình đọc hết và sẽ bổ sung những ý hay vào bài.

Nguồn tham khảo