왜?
django 에는 기본 테스트 모듈이 내재 되어 있다. 그런데 왜 pytest를 별도로 실행을 해야하는 걸까?
-
django 내장 test 는 Boiler Plate가 존재한다.
- 모든 test set이 단일 클래스(django.test.TestCase)를 상속받아 운영된다.
-
실행이 순차적으로 진행이 되다 보니 규모가 커지다 보니 느려진다.
- pytest는 멀티 실행을 지원한다.
-
코드 작성이 단순해진다.
-
Django
- 모든 test가 class 상속으로 이루어진다.
- assert 를 각 class의 method로 평가해야 한다.
-
pytest
- function 단위로 test를 작성 할 수 있다.
- 단순 assert로 test를 평가할 수 있다.
-
- Django 외 다른 Python project에도 도입이 가능하다.
- 그 외 test config를 파일로 지정해 놓을 수 있고, fixture를 정해놓고 여러 곳에서, 원하는 순서에 맞추어 실행 시켜 볼 수 있다.
설치 및 기본 설정
설치
pip install pytest-django
기본 설정
# pytest.ini Project root folder
# 아래 설정을 잡아 주지 않을 경우, pytest 실행시 에러가 난다.
[pytest]
DJANGO_SETTINGS_MODULE = yourproject.settings
실행
pytest <argument>
실행 옵션(pytest run param)
pytest.ini 에 지정을 할 수 있다.
[pytest]
addopts = --reuse-db
—db-reuse: 테스트 시작시 DB 를 만들고 삭제하는 작업을 하지 않는다. 소규모 테스트를 한다면 이게 더 중요하다.
—create-db: 테스트 시작시 DB를 새로 만든다.(models 변경 사항을 반영 할 수 있다.)
- default로 —db-reuse 기본으로 해놓고 db schema 변경시에만 설정하는걸 공식 문서에서는 추천한다.
—migration: model schema 변경이 있을 경우 migration을 진행한다. (django db 가 아닌 test db에)
—no-migration: schema 변경을 무시한다.
Markers
테스트 함수나 클래스에 메타 데이터를 쉽게 설정할 수 있다.
-
pytest.marks.db_django
- db 연결이 필요할 경우 marks 를 활용한다.
-
import pytest @pytest.mark.django_db def test_my_user(): me = User.objects.get(username='me') assert me.is_superuser
Fixture
-
rf
: django.utils.RequestFactory- middleware를 거치지 않고 바로 view로 연결 되는 request를 만든다.
- 이럴 경우, request.user가 없어서 별도로 부착을 해줘야 한다.
-
from myapp.views import my_view def test_details(rf, admin): request = rf.get('/customer/details') # Remember that when using RequestFactory, the request does not pass # through middleware. If your view expects fields such as request.user # to be set, you need to set them explicitly. # The following line sets request.user to an admin user. request.user = admin response = my_view(request) assert response.status_code == 200
client
:django.test.Client
-
def test_with_client(client): response = client.get('/') assert response.content == 'Foobar'
fixture 만들기 샘플
# user 라는 fixtrue를 만들어 다른 곳에서 사용한다. import pytest from model_bakery import baker @pytest.fixture() def user(db): return baker.bake(User) class SiteAPIViewTestSuite: # 위에서 만든 user 라는 fixture를 사용한다. # client는 pytest-django 내장 client를 지칭한다. def test_create_view(self, client, user): assert Site.objects.count() == 0 post_data = { 'name': 'Stackoverflow' 'url': 'http://stackoverflow.com', 'user_id': user.id, } response = client.post( reverse('sites:create'), json.dumps(post_data), content_type='application/json', ) data = response.json() assert response.status_code == 201 assert Site.objects.count() == 1 assert data == { 'count': 1, 'next': None, 'previous': None 'results': [{ 'pk': 1, 'name': 'Stackoverflow', 'url': 'http://stackoverflow.com', 'user_id': user.id }] }