프로젝트를 진행하다 보면 내가 짠 코드가 잘 작동하는지만 확인하는 수준에 그치게 되는데, 이러한 개발은 좋은 개발이 아니다. 작동한다는 뜻이 버그가 없다는 뜻이 아니기 때문이다. 게다가 많은 개발자들과 협업하기 위해서는 테스트코드를 작성해놔야 서로 확인하기 수월하고 문제점을 예방할 수 있다. 그렇기 때문에!! 테스트 코드는 매우 중요하다고 할 수 있다...(사실 너무너무 귀찮음)
"polls/test.py"에 다음과 같은 내용을 추가한다.
import datetime
from django.test import TestCase
from django.utils import timezone
from .models import Question
class QuestionModelTests(TestCase):
def test_was_published_recently_with_future_question(self):
"""
was_published_recently() returns False for questions whose pub_date
is in the future.
"""
time = timezone.now() + datetime.timedelta(days=30)
future_question = Question(pub_date=time)
self.assertIs(future_question.was_published_recently(), False)
-> 버그 발생!!
"polls/models.py"
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
코드를 추가해 버그를 방지 할 수 있다.
def test_was_published_recently_with_old_question(self):
"""
하루전 질문이 최신 질문인지 확인 - Flase면 테스트 통과
"""
time = timezone.now() - datetime.timedelta(days=1, seconds=1)
old_question = Question(pub_date=time)
self.assertIs(old_question.was_published_recently(), False)
def test_was_published_recently_with_recent_question(self):
"""
23시간59분전 질문이 최신 질문인지 확인 - True면 테스트 통과
"""
time = timezone.now() - datetime.timedelta(hours=23, minutes=59, seconds=59)
recent_question = Question(pub_date=time)
self.assertIs(recent_question.was_published_recently(), True)
def create_question(question_text, days):
"""
Create a question with the given `question_text` and published the
given number of `days` offset to now (negative for questions published
in the past, positive for questions that have yet to be published).
"""
time = timezone.now() + datetime.timedelta(days=days)
return Question.objects.create(question_text=question_text, pub_date=time)
class QuestionIndexViewTests(TestCase):
def test_no_questions(self):
"""
If no questions exist, an appropriate message is displayed.
"""
response = self.client.get(reverse("polls:index"))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "No polls are available.")
self.assertQuerySetEqual(response.context["latest_question_list"], [])
def test_past_question(self):
"""
Questions with a pub_date in the past are displayed on the
index page.
"""
question = create_question(question_text="Past question.", days=-30)
response = self.client.get(reverse("polls:index"))
self.assertQuerySetEqual(
response.context["latest_question_list"],
[question],
)
def test_future_question(self):
"""
Questions with a pub_date in the future aren't displayed on
the index page.
"""
create_question(question_text="Future question.", days=30)
response = self.client.get(reverse("polls:index"))
self.assertContains(response, "No polls are available.")
self.assertQuerySetEqual(response.context["latest_question_list"], [])
def test_future_question_and_past_question(self):
"""
Even if both past and future questions exist, only past questions
are displayed.
"""
question = create_question(question_text="Past question.", days=-30)
create_question(question_text="Future question.", days=30)
response = self.client.get(reverse("polls:index"))
self.assertQuerySetEqual(
response.context["latest_question_list"],
[question],
)
def test_two_past_questions(self):
"""
The questions index page may display multiple questions.
"""
question1 = create_question(question_text="Past question 1.", days=-30)
question2 = create_question(question_text="Past question 2.", days=-5)
response = self.client.get(reverse("polls:index"))
self.assertQuerySetEqual(
response.context["latest_question_list"],
[question2, question1],
)
reverse("polls:index") : Django의 리버싱 기능을 이용하여 polls앱의 index라는 이름이 붙은 URL패턴에 대한 URL경로를 반환한다.(urls.py에 있음)
self.client.get("URL") : URL을 사용하여 HTTP GET요청을 보낸다. -> views.py에서 정보를 찾고, template_name의 경로에 맞는 템플릿을 호출하게 된다. 거기에 있는 정보들을 참조할수 있게 해준다. +) views.py에 있는 context_object_name은 템플릿에 사용될 변수 이름을 지정
assert를 이용해 테스트 진행
정보) 코드가 200이면 성공
정보) 테스트 케이스에서는 각 메서드 실행시 데이터 베이스가 초기화 됨, 즉 독립적으로 실행되며 비어있는 가상 데이터 베이스로 테스트를 진행함
정보) 제너릭 뷰의 get_queryset() 메서드는 제너릭에서 제공되는 메서드임, 해당 뷰와 관련된 모든 객체를 반환, 객체를 필터링 가능
Django의 TestCase 클래스에서 사용할 수 있는 assert 메서드들을 알아보자
| 메서드 | 기능 |
|---|---|
| assertEqual(a,b) | a 와 b가 같음을 비교한다. 같지 않으면 테스트 실패 |
| assertNotEqual(a, b) | a와 b가 다름을 확인합니다. 같으면 테스트 실패. |
| assertTrue(expr) | expr가 True임을 확인. False이면 테스트 실패. |
| assertFalse(expr) | expr가 False임을 확인. True이면 테스트 실패. |
| assertIsNone(expr) | expr가 None임을 확인. None이 아니면 테스트 실패. |
| assertIsNotNone(expr) | expr가 None이 아님을 확인. None이면 테스트 실패. |
| assertIn(item, container) | item이 container에 포함되어 있음을 확인. 아니면 실패 |
| assertNotIn(item, container) | item이 container에 포함되어 있지 않음을 확인. 포함되어 있으면 테스트 실패. |
| assertContains(response, text) | response의 내용에 text가 포함되어 있음을 확인합니다. 포함되어 있지 않으면 테스트 실패. |
| assertNotContains(response, text) | response의 내용에 text가 포함되어 있지 않음을 확인합니다. 포함되어 있으면 테스트 실패. |
| assertQuerySetEqual(qs, expected) | 쿼리셋 qs와 expected가 동일한지 확인합니다. 다르면 테스트 실패. |
| assertRedirects(response, expected_url) | response가 지정된 URL로 리다이렉트되는지 확인합니다. 리다이렉트되지 않으면 테스트 실패. |
"polls/views.py"
class DetailView(generic.DetailView):
...
def get_queryset(self):
"""
Excludes any questions that aren't published yet.
"""
return Question.objects.filter(pub_date__lte=timezone.now())
"polls/tests.py"
class QuestionDetailViewTests(TestCase):
def test_future_question(self):
"""
The detail view of a question with a pub_date in the future
returns a 404 not found.
"""
future_question = create_question(question_text="Future question.", days=5)
url = reverse("polls:detail", args=(future_question.id,))
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
def test_past_question(self):
"""
The detail view of a question with a pub_date in the past
displays the question's text.
"""
past_question = create_question(question_text="Past Question.", days=-5)
url = reverse("polls:detail", args=(past_question.id,))
response = self.client.get(url)
self.assertContains(response, past_question.question_text)
python3 manage.py test polls