출시·고도화 중
Django 안내서 · 5/6
Django에는 표준 라이브러리 unittest를 바탕으로 한 테스트 도구가 들어 있습니다. 테스트용 데이터베이스를 자동으로 만들고 지우며, 브라우저 없이 요청을 보내 보는 테스트 클라이언트도 제공합니다. pytest를 선호한다면 pytest-django 플러그인으로 같은 기능을 쓸 수 있습니다.
테스트는 앱의 tests.py나 tests/ 패키지 안의 test로 시작하는 파일에 둡니다. manage.py test가 찾아서 실행합니다.
# 모든 테스트
python manage.py test
# 앱 하나, 클래스 하나, 메서드 하나
python manage.py test polls
python manage.py test polls.tests.QuestionModelTests
python manage.py test polls.tests.QuestionModelTests.test_str
# 테스트 데이터베이스를 다음 실행에도 재사용
python manage.py test --keepdb
# 여러 프로세스로 나눠 실행
python manage.py test --parallel테스트를 시작하면 Django는 설정의 데이터베이스 이름 앞에 test_를 붙인 별도 데이터베이스를 만들고 마이그레이션을 적용합니다. 실제 데이터는 건드리지 않습니다.
django.test.TestCase는 테스트 메서드마다 트랜잭션을 열고 끝나면 되돌리므로, 한 테스트에서 만든 데이터가 다른 테스트에 남지 않습니다. 여러 테스트가 함께 쓰는 데이터는 setUpTestData에서 클래스당 한 번만 만들면 빠릅니다.
# polls/tests.py
from datetime import timedelta
from django.test import TestCase
from django.utils import timezone
from .models import Question
class QuestionModelTests(TestCase):
@classmethod
def setUpTestData(cls):
cls.question = Question.objects.create(question_text="좋아하는 언어는?")
def test_str(self):
self.assertEqual(str(self.question), "좋아하는 언어는?")
def test_future_question_is_not_listed(self):
Question.objects.create(
question_text="미래 질문",
pub_date=timezone.now() + timedelta(days=30),
)
listed = Question.objects.filter(pub_date__lte=timezone.now())
self.assertQuerySetEqual(listed, [self.question])데이터베이스가 필요 없는 순수 함수라면 SimpleTestCase를 써서 더 빠르게 실행할 수 있습니다.
self.client는 가짜 브라우저처럼 GET·POST 요청을 보내고 응답을 돌려받습니다. 응답에는 상태 코드뿐 아니라 렌더링에 쓰인 템플릿과 컨텍스트도 들어 있습니다.
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse
from .models import Question
class QuestionViewTests(TestCase):
def test_index_lists_questions(self):
Question.objects.create(question_text="첫 질문?")
response = self.client.get(reverse("polls:index"))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "첫 질문?")
self.assertTemplateUsed(response, "polls/index.html")
def test_detail_returns_404_for_missing_question(self):
response = self.client.get(reverse("polls:detail", args=[999]))
self.assertEqual(response.status_code, 404)
def test_create_requires_login(self):
url = reverse("polls:create")
response = self.client.post(url, {"question_text": "새 질문?"})
self.assertEqual(response.status_code, 302)
user = get_user_model().objects.create_user("kim", password="pass1234")
self.client.force_login(user)
response = self.client.post(url, {"question_text": "새 질문?"})
self.assertEqual(Question.objects.count(), 1)force_login()은 비밀번호 확인 없이 로그인 상태를 만들어 줍니다. URL은 문자열로 적지 말고 reverse()로 만들면 URL 구조가 바뀌어도 테스트를 고칠 필요가 없습니다. 테스트 클라이언트는 기본적으로 CSRF 검사를 하지 않습니다.
특정 테스트에서만 설정을 바꾸려면 override_settings를 씁니다. 외부 API처럼 테스트 중에 실제로 부르면 안 되는 것은 unittest.mock으로 대체합니다.
from unittest import mock
from django.core import mail
from django.test import TestCase, override_settings
@override_settings(EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend")
class NotifyTests(TestCase):
def test_sends_mail(self):
mail.send_mail("제목", "본문", "from@example.com", ["to@example.com"])
self.assertEqual(len(mail.outbox), 1)
@mock.patch("polls.services.fetch_weather", return_value={"temp": 20})
def test_uses_weather(self, fetch):
from polls.services import summary
self.assertIn("20", summary())
fetch.assert_called_once()Django는 테스트 실행 중 메일 백엔드를 메모리 방식으로 바꾸므로, 위처럼 직접 지정하지 않아도 mail.outbox로 보낸 메일을 확인할 수 있습니다.
pytest의 간결한 assert 문법과 픽스처를 쓰고 싶다면 pytest-django를 설치합니다.
python -m pip install pytest pytest-django# pyproject.toml
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "mysite.settings"
python_files = ["tests.py", "test_*.py"]# polls/test_views.py
import pytest
from django.urls import reverse
from polls.models import Question
@pytest.mark.django_db
def test_index(client):
Question.objects.create(question_text="pytest 질문?")
response = client.get(reverse("polls:index"))
assert response.status_code == 200
assert "pytest 질문?" in response.content.decode()
@pytest.mark.django_db
def test_admin_index(admin_client):
response = admin_client.get("/admin/")
assert response.status_code == 200데이터베이스를 쓰는 테스트에는 @pytest.mark.django_db를 붙여야 합니다. client, admin_client, rf(RequestFactory), settings 같은 픽스처가 기본으로 제공되며, 기존 TestCase 클래스도 그대로 실행됩니다. 커버리지는 coverage run -m pytest나 pytest-cov로 측정합니다.
python manage.py test가 별도의 테스트 데이터베이스를 만들어 테스트를 실행합니다.TestCase는 테스트마다 트랜잭션을 되돌리고, setUpTestData로 공용 데이터를 한 번만 만듭니다.self.client와 reverse()로 뷰를 요청 단위로 검증합니다.@pytest.mark.django_db와 client 픽스처로 같은 테스트를 더 짧게 쓸 수 있습니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.