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

0%

Writing your first Django app, part5 - 4편

View 테스트 하기


우리가 작성한 polls 어플리케이션은 구분을 잘 못합니다.


이게 무슨 이야기냐면, polls app은 아무 질문이나 발행하고, pub_date 필드가 미래의 값인것도 발행 가능합니다. 이점은 개선 되어야 할점입니다.



pub_date 를 미래의 날짜로 설정한다는것은,


Question 은 그때 발행은 되지만, 해당 pub_date 날짜가 올때까지 보여지지 않아야 합니다.



View 를 위한 테스트



버그를 위에서 고쳤을때, 테스트를 처음 써주고, 해당 버그를 고치는 코드를 써주었습니다. 하지만, 이것은 테스트 기반 개발 방식의 예시입니다. 어떤 순서로 일을 하던 상관은 없습니다.



우리의 첫번째 테스트에서는, 코드의 내부 동작에 대해 조금 더 집중하였습니다. 이번 테스트에서는, 코드의 동작이 웹브라우저를 통한 사용자가 경험할 법한 것들에 대해서 확인 하고 싶습니다.


뭐든지 고치기 전에, 몇가지 도구들을 둘러보고 갑니다.


Django 테스트 클라이언트


장고는 테스트 클라이언트를 제공합니다. 뷰에서 사용자가 코드와 상호작용 하는것을 시뮬레이션 할수 있습니다.


tests.py 에서도 사용할수 있고, 심지어 shell 에서도 사용이 가능합니다.


shell 에서 먼저 시작해 보도록 합시다. shell 에서 tests.py 에서 불필요한것들을 수행해 보려 합니다.



첫번째로, shell 에서 테스트 환경을 설정해 줘야 합니다

튜토리얼에서는, shell 을 사용하나, 저는 shell_plus 를 사용했습니다.



1
python manage.py shell_plus

1
2
3
In [1]: from django.test.utils import setup_test_environment                                                                                                              

In [2]: setup_test_environment()


setup_test_environment() 는 템플릿 렌더러를 설치합니다. template renderer 는 response.context 같은 리스폰스에 추가적인 속성들을 실험해 볼수 있게 해줍니다. 하지만, 이 메서드는 테스트 데이터베이스를 생성하지 않습니다.



따라서, 이것을 실행 하면, 이미 존재하는 데이터베이스를 기반으로 실행이 되고, 결과는 어떤 질문들을 생성했느냐에 따라서 살짝 달라질수 있습니다. settings.py 에 있는 TIME_ZONE 설정이 잘못 되어 있을 경우, 전혀 예상치 못한 결과가 나타날수 있습니다.


다음은, test 클라이언트 클래스를 가져 오는 겁니다.



1
2
In [3]: from django.test import Client                                                                                                                               
In [4]: client = Client()


여기까지 준비가 되었으면, 우리는 클라이언트에 어떠한 일을 수행해 달라고 할수 있습니다.



1
2
3
4
5
6
7
8
9
In [5]: # / 에서 response 를 가져옴                                                                                                                                       

In [6]: response = Client.get('/')
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-6-4f89fae61f34> in <module>
----> 1 response = Client.get('/')

TypeError: get() missing 1 required positional argument: 'path'



/로 접속하면, 접속이 되질 않지만, /polls/ 를 입력하면 접속이 됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
In [8]: response = client.get('/polls/')                                                                                                                             
In [9]: response.status_code
Out[9]: 200
# reverse() 함수를 사용하여 하드코드된 URL 을 피할수 있습니다.

In [11]: from django.urls import reverse

In [12]: response = client.get(reverse('polls:index'))
In [13]: response.status_code
Out[13]: 200

In [14]: response.content Out[14]: b'\n <ul>\n \n <li><a href="/polls/specifics/1/">What&#x27;s up?</a></li>\n \n </ul>\n'

In [15]: response.context['latest_question_list']
Out[15]: <QuerySet [<Question: What's up?>]>

shell 을 통해서 테스트 환경을 테스트 해보았습니다.


View 향상 시키기



polls 의 리스트는 발행되지 않은 polls 도 표시합니다. 이건 문제가 될수 있으니, 수정해주어야 합니다.


part4 에서 ListView 를 기반으로 하는 클래스 기반 뷰를 소개했었습니다.


polls/views.py 파일을 열어서, IndexView 안의 get_queryset() 메서드를 수정해줍니다. timezone.now() 와 날짜를 비교하여 체크하게 만들겁니다. 일단 polls/views.py 에 timezone 가져옵니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic

from .models import Choice, Question

from django.utils import timezone

class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'

def get_queryset(self):
"""
가장 최근의 5개의 발행된 질문들을 반환합니다
미래 날짜에 발행된 질문들은 반환되지 않습니다
"""
return Question.objects.filter(pub_date__lte=timezone.now()).order_by('-pub_date')[:5]

Question.objects.filter(pub_date__lte=timezone.now()) 는 pub_date 가 timezone.now 보다 작거나 같은, 즉 timezone.now 보다 이르거나 같은 시간대를 가진 QUestion 을 반환합니다.


마치며..


너무 길어질것 같아서 포스팅을 나눕니다. 다음 포스팅에 이어 합니다.