본문 바로가기
Python/Django

[Django] Test

by 혀나Lee 2016. 10. 12.

# Django Unit tests


- Python 3.3 밑의 버전의 경우, unittest.mock 모듈의 백포트를 설치해야 함.

- Django의 버전이 낮을 경우, settings file을 명시해 주어야 함.

- Selenium 테스트 시, Selenium 2, Firefox, Python >= 2.6이 필요하며 selenium package(version > 2.13) 설치 필수.

     -> 실행 시에 --selenium 옵션으로 실행한다.

         $ ./runtests.py --settings=test_sqlite --selenium admin_inlines


1. Django Unit test의 특징


  • Django는 project나 app을 생성하게 되면 기본적으로 tests.py라는 파일이 생성된다.(unit test 코드를 이 파일에 작성)
  • Python의 Unittest와 동일하게 작성. (자동으로 테스트 데이터베이스를 생성해주고 끝나면 삭제 해 줌.)
import unittest
class TestClass1(unittest.TestCase):
    def test1(self):
        self.assertTrue(...)
        self.assertFalse(...)
        ...

python manage.py test 로 실행가능 하며 각 앱만 unittest 구동하려하면 python manage.py test AppName 으로 실행할 수 있다.

  • 함수 명은 반드시 test로 시작해야 함.
  • 테스트 순서는 기본적으로 클래스(class)나 메서드(method)의 이름에 의존
class Test00Basic(unittest.TestCase):
    def test0000LibraryAAA(self):
        ....
    def test0001LibraryBBB(self):
        ...
class Test01OtherLibrary(unittest.TestCase):
    def test0100ABC(self):
        ...

(warning) 순차적으로 실행되어야 할 테스트가 있다면 class와 method의 이름에 고유번호를 메기는 방식 적용.(오름차순)

2. 장고 테스트 실행 프레임워크(test-execution framework)

  • django.test.TestCase

django.test.TestCase 는 각각 테스트의 트렌젝션의 분리를 제공합니다. 이 모듈은 클래스 기반 접근법으로 테스트를 정의합니다.

from django.test import TestCase
 
class post(TestCase):
    print('test')

  • django.test.Client

django.test.Client 는 URL에 low-level HTTP 부터 페이지 컨텐츠까지 GET, POST 요청에 대한 응답을 시뮬레이팅 해줍니다.


from django.test import TestCase
from django.test import Client
 
class post(TestCase):
    c = Client()
    response = c.get('/board/get_post/32')
    print(response.status_code)
 
    response = c.post('/board/write_post/', {'author_id': '아이디', 'post_title': '제목', 'post_contents': '내용'})
    print(response.status_code)


# PyTest(py.test)


1. 장점

  • DB 파일을 매번 새로 만들지 않는다. -> 테스트 실행 속도 향상
  • 테스트 간 의존성을 관리할 수 있다.
  • 테스트 동시 실행도 가능하다. (pytest-xdist 필요)
  • 기존 테스트들과 호환된다.

2. 설치

pip install pytest-django

pytest 뿐만 아니라 django의 테스트 코드를 인식하는 플러그인도 설치된다.


3. 병렬 실행

pip install pytest-xdist

"pytest-xdist" 플러그인을 설치하여 테스트들을 병렬적으로 실행할 수 있다.

py.test -n 3	# 테스트가 3개씩 병렬적으로 실행됨


# test


test 함수 만들기 전 File : mysite.zip (plus) https://docs.djangoproject.com/en/1.7/intro/tutorial01/

1. 테스트 예제

import datetime

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


class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    def __str__(self):
        return self.question_text

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


class Choice(models.Model):
    question = models.ForeignKey(Question)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __str__(self):
        return self.choice_text


import datetime

from django.utils import timezone
from django.test import TestCase

from polls.models import Question


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.assertEqual(future_question.was_published_recently(), False)

polls.models.py 의 Question 모델 클래스에 있는 was_pulished_recently() 함수를 테스트하기 위한 파일이다.


2. 테스트 실행

  • 명령어 실행

아래의 명령어를 통해 테스트를 실행할 수 있다.

python manage.py test polls


C:\Users\hyuna\PycharmProjects\mysite>python manage.py test polls
Creating test database for alias 'default'...
F
======================================================================
FAIL: test_was_published_recently_with_future_question (polls.tests.QuestionMethodTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\hyuna\PycharmProjects\mysite\polls\tests.py", line 13, in test_was_published_recently_with_future_question
    self.assertEqual(future_question.was_published_recently(), False)
AssertionError: True != False
----------------------------------------------------------------------
Ran 1 test in 0.002s
FAILED (failures=1)
Destroying test database for alias 'default'...


  • 파이참 실행

파이참에서 테스트함수에서 마우스 우클릭을 하여 테스트를 실행할 수 있다.

테스트를 실행하면 다음과 가은 결과를 얻을 수 있다.

그리고 파이참에서는 다시 테스트를 쉽게 실행할 수 있도록 저장해준다.

  • 실행 결과

- python manage.py test polls : polls 앱 안에 있는 테스트들을 모두 찾는다.

- django.test.TestCase 의 서브클래스를 발견한다.

- 테스팅 목적의 특수 데이터베이스를 만든다.

- test로 시작되는 이름을 가진 테스트 메서드를 찾는다

- test_was_published_recently_with_future_question 에서 pub_date 필드가 30일 미래로 되어 잇는 Question 인스턴스를 생성한다.

- assertEqual() 메서드를 사용하여 was_published_recently() 가 True를 리턴하고 비교값(False)와 다르다는 것을 확인했다.

  • 버그 수정 후, 테스트 결과
# 에러가 발생하는 함수 수정
def was_published_recently(self):
    now = timezone.now()
    return now - datetime.timedelta(days=1) <= self.pub_date <= now

버그 수정 후, 테스트를 통과하는 것을 확인할 수 있다.


3. 테스트 예제(2)와 실행

import datetime

from django.utils import timezone
from django.test import TestCase

from polls.models import Question


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.assertEqual(future_question.was_published_recently(), False)

    def test_was_published_recently_with_old_question(self):
        time = timezone.now() - datetime.timedelta(days=30)
        old_question = Question(pub_date=time)
        self.assertEqual(old_question.was_published_recently(), False)

    def test_was_published_recently_with_recent_question(self):
        time = timezone.now() - datetime.timedelta(hours=1)
        recent_question = Question(pub_date=time)
        self.assertEqual(recent_question.was_published_recently(), True)

좀 더 확실한 테스트를 위해, 테스트 함수를 추가했다.

"Run/Debug Configurations"에서 테스트를 한 번에 수행할 수 있도록 추가한다.

테스트 결과, 3개의 테스트 모두 성공한 것을 확인할 수 있다.



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

[Django] File upload (FileField, ImageField)  (0) 2016.10.19
[Django] uwsgi + nginx in ubuntu  (0) 2016.10.18
[Django] Extra  (0) 2016.10.06
[Django] Filter vs Exclude  (0) 2016.10.04
[Django] Custom Model Fields  (0) 2016.09.29

댓글