알쓸전컴(알아두면 쓸모있는 전자 컴퓨터)

DjangoTEST 하기 본문

Web /Django

DjangoTEST 하기

백곳 2017. 8. 15. 11:12

TEST 하기 


여기서 테스트 할것은 polls/model.py 의 Question 의 함수에서 was_published_recently()가 Question.pub_date 가 오늘 날짜인지 구분하는 

함수를 만들것 입니다. 테스트를 위해 일부러 잘못된 코드를 작성 하도록 할것 입니다. 

polls/models.py

from django.db import models
import datetime
from django.utils import timezone

# Create your models here.
class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
     
class Choice(models.Model):
     question = models.ForeignKey(Question, on_delete=models.CASCADE)
     choice_text = models.CharField(max_length=200)
     votes = models.IntegerField(default=0)


위와 같은 코드가 모델로 작성 되어 있습니다. 


일단 Python 콘솔 에서 아래와 같이 입력하면 

import django

import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")

django.setup()

from django.test.utils import setup_test_environment

setup_test_environment()

from polls.models import Question

future_question = Question(pub_date=timezone.now() + datetime.timedelta(days=30))

future_question.was_published_recently()

TRUE 로 결과가 나옵니다. 하지만 future_question 은 30일 미래 입니다. 

FALSE가 나와야 합니다. 


이를 콘솔이 아니라 직접 테스트 코드를 입력 합니다. 

테스트트 코드를 만들때는 class 는 TestCase 을 상속 받고 테스트할 함수의 이름은 test_ 로 시작 하여야 합니다. 

polls/tests.py

import datetime
from django.utils import timezone
from django.test import TestCase
from .models import Question

class QuestionMethodTests(TestCase):

    def test_was_published_recently_with_future_question(self):
        """
        was_published_recently() should return 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)


$ python manage.py test polls

을 실행 시키면 

Creating test database for alias 'default'...

System check identified no issues (0 silenced).

F

======================================================================

FAIL: test_was_published_recently_with_future_question (polls.tests.QuestionMethodTests)

----------------------------------------------------------------------

Traceback (most recent call last):

  File "/home/bhkim/mysite/polls/tests.py", line 12, 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.002s


FAILED (failures=1)

Destroying test database for alias 'default'...

위와 같이 나옵니다. 

어디서 오류와 나왔는지 상세히 알려줍니다. 


그러니 버그를 수정하겠습니다.

버그 수정 


polls/models.py

from django.db import models
import datetime
from django.utils import timezone

# Create your models here.
class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now
     
class Choice(models.Model):
     question = models.ForeignKey(Question, on_delete=models.CASCADE)
     choice_text = models.CharField(max_length=200)
     votes = models.IntegerField(default=0)


위와 같이 수정 하고 나서 

$ python manage.py test polls

Creating test database for alias 'default'...

System check identified no issues (0 silenced).

.

----------------------------------------------------------------------

Ran 1 test in 0.002s


OK

Destroying test database for alias 'default'...


위와같은 결과가 나옵니다. 

위에 결과에서 Creating test database for alias 'default'...  을 나온것 처럼 기존에 DB에서 데이터를 사용하지 않고 구조만 사용하는 

테스트 전용 DB를 만들어서 테스트를 진행해 줍니다.


이러한 기세를 모아서 2개 테스트 코드를 더 입력 하겠습니다. 

polls/tests.py

from django.test import TestCase
import datetime
from django.utils import timezone
from django.test import TestCase
from .models import Question

# Create your tests here.
class QuestionMethodTests(TestCase):
    def test_was_published_recently_with_future_question(self):
        time = timezone.now() + datetime.timedelta(days=30)
        future_question = Question(pub_date=time)
        self.assertIs(future_question.was_published_recently(), False)
    def test_was_published_recently_with_old_question(self):
        """
        was_published_recently() should return False for questions whose
        pub_date is older than 1 day.
        """
        time = timezone.now() - datetime.timedelta(days=30)
        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() should return True for questions whose
        pub_date is within the last day.
        """
        time = timezone.now() - datetime.timedelta(hours=1)
        recent_question = Question(pub_date=time)
        self.assertIs(recent_question.was_published_recently(), True)    


test_was_published_recently_with_old_question,test_was_published_recently_with_recent_question 의 2개의 함수를 추가 합니다. 

$ python manage.py test polls

Creating test database for alias 'default'...

System check identified no issues (0 silenced).

...

----------------------------------------------------------------------

Ran 3 tests in 0.003s


OK

Destroying test database for alias 'default'...




테스트 클라이언트

클라이언트로 접속 했을때 상황등을 테스트 할수가 있습니다. 

콘솔로 실행 해보도록 하겠습니다. 

In : import django

In :import os

In :os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")

In :django.setup()

In :from django.test.utils import setup_test_environment

In :setup_test_environment()

In :from django.test import Client

In :client = Client()

In :response = client.get('/')

In :response.status_code

Out : 404

In :response.content

Out : b'\n<!DOCTYPE html>\n<html lang="en">\n<head>\n  <meta http-equiv="content-type" content="text/html; charset=utf-8">\n  <title>Page not found at /</title>\n  <meta name="robots" content="NONE,NOARCHIVE">\n  <style type="text/css">\n    html * { padding:0; margin:0; }\n    body * { padding:10px 20px; }\n    body * * { padding:0; }\n    body { font:small sans-serif; background:#eee; }\n    body>div { border-bottom:1px solid #ddd; }\n    h1 { font-weight:normal; margin-bottom:.4em; }\n    h1 span { font-size:60%; color:#666; font-weight:normal; }\n    table { border:none; border-collapse: collapse; width:100%; }\n    td, th { vertical-align:top; padding:2px 3px; }\n    th { width:12em; text-align:right; colo......

In :from django.urls import reverse

In :reverse('polls:index')

Out :'/polls/'

In :response = client.get(reverse('polls:index'))

In :response.status_code

Out :200

In :response.content

Out :b'\n    <ul>\n    \n        <li><a href="/polls/6/">what question6</a></li>\n    \n        <li><a href="/polls/5/">what question5</a></li>\n    \n        <li><a href="/polls/4/">what question4</a></li>\n    \n        <li><a href="/polls/3/">what question3</a></li>\n    \n        <li><a href="/polls/2/">what question2</a></li>\n    \n    </ul>\n\n'

In : response.context_data['latest_question_list']

Out : <QuerySet [<Question: Question object>, <Question: Question object>, <Question: Question object>, <Question: Question object>, <Question: Question object>]>


위와 같이 Client 로 테스트 할수가 있습니다. 


수정 View test

일부러 View 를 수정한 다음에 테스트 코드를 작성 하겠습니다. 


polls/views.py


from django.shortcuts import get_object_or_404, render

# Create your views here.
from django.http import HttpResponseRedirect,HttpResponse
from .models import Question,Choice
from django.urls import reverse
from django.views import generic
from django.utils import timezone

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}   
    return render(request, 'polls/index.html', context)

def detail(request,question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

def results(request,question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/results.html', {'question': question})
 
def vote(request,question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        # Redisplay the question voting form.
        return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "You didn't select a choice.",
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()
        # Always return an HttpResponseRedirect after successfully dealing
        # with POST data. This prevents data from being posted twice if a
        # user hits the Back button.
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
	
class IndexView(generic.ListView):
    template_name = 'polls/index.html'
    context_object_name = 'latest_question_list'

    def get_queryset(self):
        """Return the last five published questions."""
        return Question.objects.filter(pub_date__lte=timezone.now()).order_by('-pub_date')[:5]

class DetailView(generic.DetailView):
    model = Question
    template_name = 'polls/detail.html'

class ResultsView(generic.DetailView):
    model = Question
    template_name = 'polls/results.html'    


수정을 해줍니다. 


이를 테스트 하기 위해서 

polls/tests.py


from django.test import TestCase
import datetime
from django.utils import timezone
from django.test import TestCase
from .models import Question
from django.urls import reverse

def create_question(question_text, days):
    """
    Creates 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)

# Create your tests here.
class QuestionMethodTests(TestCase):
    def test_was_published_recently_with_future_question(self):
        time = timezone.now() + datetime.timedelta(days=30)
        future_question = Question(pub_date=time)
        self.assertIs(future_question.was_published_recently(), False)
    def test_was_published_recently_with_old_question(self):
        """
        was_published_recently() should return False for questions whose
        pub_date is older than 1 day.
        """
        time = timezone.now() - datetime.timedelta(days=30)
        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() should return True for questions whose
        pub_date is within the last day.
        """
        time = timezone.now() - datetime.timedelta(hours=1)
        recent_question = Question(pub_date=time)
        self.assertIs(recent_question.was_published_recently(), True)    
class QuestionViewTests(TestCase):
    def test_index_view_with_no_questions(self):
        """
        If no questions exist, an appropriate message should be 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_index_view_with_a_past_question(self):
        """
        Questions with a pub_date in the past should be displayed on the
        index page.
        """
        create_question(question_text="Past question.", days=-30)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(
            response.context['latest_question_list'],
            ['<question: question="" object="">']
        )

    def test_index_view_with_a_future_question(self):
        """
        Questions with a pub_date in the future should not be 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_index_view_with_future_question_and_past_question(self):
        """
        Even if both past and future questions exist, only past questions
        should be displayed.
        """
        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: question="" object="">']
        )

    def test_index_view_with_two_past_questions(self):
        """
        The questions index page may display multiple questions.
        """
        create_question(question_text="Past question 1.", days=-30)
        create_question(question_text="Past question 2.", days=-5)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(
            response.context['latest_question_list'],
            ['<question: question="" object="">', '<question: question="" object="">']
        )     


다음과 같이 test 코드를 작성 한뒤에 테스트를 하면 됩니다. 

$ python manage.py test polls

Creating test database for alias 'default'...

System check identified no issues (0 silenced).

........

----------------------------------------------------------------------

Ran 8 tests in 0.051s


OK

Destroying test database for alias 'default'...


만약 오류가 나온다면 보는 법입니다. 

위의 코드에서 58번째 줄을 
['<Question: Question object>']  -> ['<Question: Question obj>'] 
로 바꾸고 테스트 하면 

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
....F...
======================================================================
FAIL: test_index_view_with_a_past_question (polls.tests.QuestionViewTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/bhkim/mysite/polls/tests.py", line 58, in test_index_view_with_a_past_question
    ['<Question: Question obj>']
  File "/home/bhkim/anaconda3/envs/djangotest/lib/python3.5/site-packages/django/test/testcases.py", line 972, in assertQuerysetEqual
    return self.assertEqual(list(items), values, msg=msg)
AssertionError: Lists differ: ['<Question: Question object>'] != ['<Question: Question obj>']

First differing element 0:
'<Question: Question object>'
'<Question: Question obj>'

- ['<Question: Question object>']
?                          ---

+ ['<Question: Question obj>']

----------------------------------------------------------------------
Ran 8 tests in 0.064s

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

위와 같이 디버깅 코드로 잘못된 부분에 대한 설명이 나오고 이에 대해 수정 할수 있습니다.



'Web > Django' 카테고리의 다른 글

django bootstrap 테마 적용  (4) 2017.09.10
django css 적용하기  (0) 2017.08.15
제너릭 뷰 사용하기  (0) 2017.08.14
django HttpResponseRedirect 하기  (0) 2017.08.14
django 간단한 폼 만들기  (0) 2017.08.12
Comments