코드의 동작을 체크하는 작업으로 많은 사람들과 공동작업을 하는 경우 반드시 필요한 기능이다. 수십, 수백개의 컴포넌트를 자동으로 테스트 하여 시간을 절약 할 수 있고 기능을 명확히 하여 앞으로 생길 문제를 밝혀낼 수 있다.
models.py에 데이터가 최근 데이터인지 아닌지를 확인하는 테스트를 해보겠다. 현재 날짜로 부터 미래의 날짜는 최근 데이터가 아닌 FALSE 데이터로 인식한다
mysite에서 Shell 모드를 실행한다.
다음 명령어를 통해 현재 상태를 확인한다.
>>> import datetime
>>> from django.utils import timezone
>>> from polls.models import Question
>>> # create a Question instance with pub_date 30 days in the future
>>> future_question = Question(pub_date=timezone.now() + datetime.timedelta(days=30))
>>> # was it published recently?
>>> future_question.was_published_recently()
True
애플리케이션 안의 test.py 파일에 테스트 할 내용을 작성해준다.
클래스 이름은 Test로 시작하는게 좋으며 TestCase를 상속해놓아야 한다.
# /workspace/Edu_02/mysite/polls/tests.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)
쉘모드로 접속하여 아래 코드로 Test를 자동으로 수행해본다.
$ python manage.py test polls
AssertionError가 나올 것이다. 예상했던 결과값이 False가 아닌 True 이기 때문이다. 테스트를 동과하면 OK메세지가 뜬다.
발생할 수 있는 모든 경우에 대해 테스트코드를 작성해 놓는 것이 좋다. 코드의 양과 상관없이 여러번 체크가 가능하게 결과값은 다를지언정 같은 내용을 테스트 하는 여러 테스트를 수행하는 것도 좋은 방법니다.
def test_was_published_recently_with_old_question(self):
"""
was_published_recently() returns False for questions whose pub_date
is older than 1 day.
"""
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):
"""
was_published_recently() returns True for questions whose pub_date
is within the last day.
"""
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)
뷰는 Request를 받고 Response를 해주는 것이 핵심이다. Django에서는 뷰레벨에서 사용자를 시뮬레이트 하기 위해 클래스 Client를 제공하며 이 테스트 클라이언트를 test.py 혹은 Shell 모드에서 사용가능하다. 일단 쉘을 이용한 테스트 방법을 알아본다.
쉘 모드로 접속한다.
테스트에 필요한 추가적인 속성을 사용할 수 있도록 setup_test_enviroment()를 설치한다.
>>> from django.test.utils import setup_test_environment
>>> setup_test_environment()
테스트 클라이언트 클래스를 import 한다.
>>> from django.test import Client
>>> # create an instance of the client for our use
>>> client = Client()
이제 아래와 같이 뷰를 테스트 해볼 수 있다.
>>> # get a response from '/'
>>> response = client.get('/') # 조회를 위해 호출
Not Found: /
>>> response.status_code # 서버의 응답상태 반환
404
>>> from django.urls import reverse
>>> response = client.get(reverse('polls:index'))
>>> response.status_code # 서버응답이 성공하면 200을 반환한다.
200
>>> response.content
b'\n <ul>\n \n <li><a href="/polls/1/">What's up?</a></li>\n \n </ul>\n\n'
>>> response.context['latest_question_list']
<QuerySet [<Question: What's up?>]>
포괄적인 테스트를 위해 Test Class를 만들어 사용할 수도 있다. 가능한 모든 상황을 코딩해놓고 정상응답 하는지, 어떤 값이 반환 될 것인지 등을 미리 정의해두고 그 조건에 맞으면 테스트가 통과되는 방식이다. 여러 테스틀 함으로 하드코딩하기 보단 반복되는 값에 대해서 미리 정의를 해놓고 가져와서 쓰는 방식이 추천된다
from django.urls import reverse
def create_question(question_text, days):
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):
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):
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):
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):
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):
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],
)