당신은 멋쟁이, 우리는 장고쟁이~

0%

Writing your first Django app, part5 - 3편

첫번째 테스트 작성하기


버그찾기


운좋게도, polls 어플리케이션에는 고쳐야할 작은 버그가 있습니다.


Question.was_publishd_recently() 메서드는, Question 이 어제 이전에 발행되었거나, Question 의 pub_date 필드가 미래일일 경우에 True 를 반환합니다.


쉘을 통해성 버그를 확인하고 확실하게 찾아냅시다. shell 을 사용해서 날짜가 미래의 날짜로 지정된 질문의 메서드를 확인 합니다.


1
python manage.py shell_plus




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
In [1]: import datetime                                                                                                                                                   

In [2]: from django.utils import timezone

In [3]: from polls.models import Question

In [4]: # pub_date 이 30일 미래 날짜를 가진 Question 인스턴스 생성

In [5]: future_question = Question(pub_date=timezone.now() + datetime.timedelta(days=30))

In [6]: # 이게 최근에 발행 됬나요?

In [7]: future_question.was_published_recently()
Out[7]: True

In [8]: # 최근 발행된 질문이 아닌데도 True 가 표시됩니다. 버그 입니다.

발행일짜가 30일 이후로 되어 있는데도, 최근인지 확인하는 메서드는 True 를 반환합니다.


이것은 명백하게 잘못되어 있는것입니다.


버그를 노출시키는 테스트


방금 shell 에서 진행 한것은, 자동화 테스트가 문제점에 대해서 정확히 무엇을 할수 있는지 테스트 해본 겁니다.


자, 그럼 자동화 테스트로 전환을 해봅시다.


관례적으로 어플리케이션의 테스트는 어플리케이션의 tests.py 파일에 작성합니다. 테스트 시스템은 자동으로 테스트 코드들을 test 로 시작하는 파일에서 찾습니다.


polls/tests.py 파일을 생성하고

아래 코드를 작성해 줍니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from django.test import TestCase 
import datetime
from django.utils import timezone

from .models import Question

# Create your tests here


class QuestionModelTests(TestCase):

def test_was_published_recently_with_future_question(self):
"""
was_published_recently() 가 pub_date 가 미래 날짜로 지정되어 있는 질문들에 대해서 False 를 반환함
"""
time = timezone.now() + datetime.timedelta(days=30)
future_question = Question(pub_date=time)
self.assertIs(future_question.was_published_recently(), False)


여기에 우리는 django.test.TestCase 서브 클래스를 만들고, pub_date 가 미래 날짜인 Question 인스턴스를 만들어 줍니다. 그 후에, 값이 False 여야 하는 was_published_recently() 의 결과를 체크합니다.



테스트 실행하기


터미널에서 테스트를 실행 할수 있습니다.


1
python manage.py test polls


실행해 보면, 아래와 같은 스크린을 볼수 있습니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F
======================================================================
FAIL: test_was_published_recently_with_future_question (polls.tests.QuestionModelTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/dhkang/django_tutorial/mysite/polls/tests.py", line 19, in test_was_published_recently_with_future_question
self.assertIs(future_question.was_published_recently(), False)
AssertionError: True is not False

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=1)
Destroying test database for alias 'default'...


무엇이 발생했냐면..


  1. manage.py test polls 명령어를 실행하여, polls 앱 안에 test 를 찾았습니다

  2. django.test.TestCase 클래스의 서브 클래스를 찾았습니다

  3. 테스트의 목적을 수행할 특수한 데이터베이스를 하나 생성했습니다

  4. test 로 시작하는 테스트의 메서드를 찾습니다

  5. test_was_published_recently_with_future_question 안에 Question 인스턴스를 하나 생성합니다. 이때, pub_date 는 30일 이후의 날짜로 지정해서 생성합니다

  6. assertIs() 함수를 사용해서, False 가 반환되어야 하는데, was_published_recently() 가 True 를 반환하는것을 발견합니다


테스트는 어떤 테스트가 실패했고, 어느 줄에서 실패가 발생했는지 알려줍니다.


버그 수정하기


우리는 이미 문제점이 무엇인지 알고 있습니다.


Question.was_published_recently() 는

pub_date 가 미래의 날짜일 경우,

False 를 반환 해야 합니다.

polls/models.py 의 이 메서드를 수정해 줍니다.



1
2
3
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now

수정을 마쳤으면, 테스트를 다시 실행 해 줍니다. python manage.py test polls


1
2
3
4
5
6
7
8
9
python manage.py test polls 
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK
Destroying test database for alias 'default'...

버그를 확인한 뒤에, 버그를 노출 시키는 테스트를 작성 하였고.


버그를 코드에서 수정하여, 테스트를 통과하게 만들었습니다.


향후, 다른 많은 부분들이 우리의 어플리케이션에서 잘못될수 있습니다. 하지만, 우리는 이 버그를 다시 가지지 않을것입니다. 왜냐하면, 테스트를 실행 시키기만 하면, 바로 버그에 대한 경고를 받을 수 있기 때문입니다.

이 부분에 대해서 고쳐졌다고 볼수 있습니다.



좀더 다양한 테스트



was_published_recently() 를 조금 더 고쳐보기로 합니다. 사실 이것은 긍정적인 의미를 가진 당황스러움 이 있습니다.


polls/tests.py 파일을 열고, 두개의 테스트를 같은 클래스에 추가해 줍니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def test_was_published_recently_with_old_question(self):
"""
was_published_recently() returns False for question 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 = Quesiton(pub_date=time)
self.assertIs(recent_question.was_published_recently(), True)

이제 Question.was_published_recently() 가 과거, 현재, 미래 날짜에 발행된 질문들에 대한 체크에 센스 있는 값을 반환 하는지 봅니다.


python manage.py test polls를 실행해 봅니다. 3개의 테스트를 진행 했고, OK 가 표시되는것을 확인 할수 있습니다.


1
2
3
4
5
6
7
8
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
...
----------------------------------------------------------------------
Ran 3 tests in 0.002s

OK
Destroying test database for alias 'default'...



다시 한번, 튜토리얼에서 만든 polls 어플리케이션은 매우 최소한의 단위를 가진 어플리케이션 입니다.


하지만, 향후에는 더 복잡함이 자라날것이고, 다른 코드들과 상호 작용을 함에 있어서, 더 복잡해 질것입니다.

우리는 우리가 작성한 코드가 예상대로 동작할것이라는 어느정도의 보장성을 가질수 있습니다.



마치며.


테스트를 진행 할때, 기억해야 하는 포인트는,,



  1. 앱안에 tests.py 파일 생성
  2. django.test.TestCase 클래스를 상속받는 클래스를 작성
  3. 클래스 안에 test 로 시작하는 메서드를 작성
  4. 해당 메서드 안에서, 테스트 할 인스턴스 생성
  5. assertIs 함수를 사용해서, 테스트가 원하는값을 반환하는지 확인
  6. 버그 수정하기