반응형


파트 5에서는 장고의 테스트 작성에 대해서 소개를 해줍니다.

장고뿐만 아니라 모든 소프트웨어 작성 시 테스트케이스 작성은 필수 입니다.

테스트는 코드 동작을 위한 간단한 루틴입니다. 

함수의 대한 리턴 값이 기대 되어지는 값이 리턴이 되는지? 사용자 입력값의 따른 시퀀스가 제대로 만들어지는가? 이러한 테스트 케이스들을 작성하여 쉘에서 명령어 하나로 테스트를 자동적으로 진행합니다.

테스트 케이스를 한번 작성 해놓으면 코드 수정 이후에도 원래 의도대로 코드가 동작하는지를 손쉽게 테스트 할 수 있습니다.


테스트가 필요한 이유 4가지

1. 테스트는 당신의 시간을 절약해줍니다.

2. 문제들을 미리 방지 합니다.

3. 당신의 코드를 더욱 매력적으로 만들어 줍니다.

4. 팀 작업을 돕습니다.


테스트 케이스를 작성해봅시다.

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)


django.test.TestCase의 서브클래스로 만들어 줍니다.

우리가 의도한 값은 최근 발행 게시물인 경우가 True가 나와야지 30일을 더한 미래 값을 만들어 주면 False가 나오길 원합니다.

원하는 값이 False 값이 나와야되는데 True값이 나오면 테스트 Fail이 발생합니다.


테스트 실행

python manage.py test polls

테스트가 실패 하였습니다.

아래와 같이 함수를 수정하고 테스를 실행하면 성공 할 것입니다.


polls/models.py

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


테스트케이스는 많이 작성해도 부족합니다.

아래와 같이 was_published_recently()에 대한 테스트 케이스를 작성해 봅시다.

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)


뷰 테스트 작성해보기.

유틸기능을 보이는 함수 이외에도 뷰도 테스를 진행 할 수 있습니다.

이전에는 미래 날짜의 게시물은 보이면 안되는데 보이게 되는 문제가 발생 하고 있습니다.


수정을 필요로 합니다.

polls/views.py

def get_queryset(self):
    """
    Return the last five published questions (not including those set to be
    published in the future).
    """
    return Question.objects.filter(
        pub_date__lte=timezone.now()
    ).order_by('-pub_date')[:5]

필터를 사용하여 현재 시간 보다 작은 질문들을 가져 옵니다.


뷰에 대한 테스트 케이스를 추가 해줍니다.

polls/test.py

from django.urls import reverse
.....

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.
        """
        create_question(question_text="Past question.", days=-30)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(
            response.context['latest_question_list'],
            ['']
        )

    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.
        """
        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'],
            ['']
        )

    def test_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'],
            ['', '']
        )

test_no_questions : No polls are available. 값을 확인 합니다.

test_past_question : 질문들이 리스트에 나오는지 확인합니다.

test_future_question : 미래질문을 만들고 값이 나오면 안되는 것을 확인합니다.


DetailView 테스트 하기.

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)

이외에도 여러 경우에 대해서 테스트를 작성 해야겠습니다.



테스트 작성은 많이 할 수록 더욱 좋다.


테스트 작성 시 아래 3가지를 명심하자.

- 각각의 모델 및 뷰를 기준으로 테스트 클래스의 분리

- 당신이 원하는 테스트의 상태 값을 위해서 테스트 함수의 분리

- 함수 설명을 위한 테스트 함수 이름


튜토리얼 습득 뿐만 아니라 배울점

1. 테스트 케이스 작성 (test-driven development)

2. TDD

3. pytest

반응형

+ Recent posts