[Django] Pytest

GreenBean·2022년 4월 12일
1
post-thumbnail

Pytest

  • pytest-djangodjango 프로젝트에서 pytest를 사용하기 위한 플러그인
    • pytestpytest-djangoStandard Django Test suiteNose Test suite 호환됨
    • 단, 테스트 실행은 djangomanage.py test를 사용하지 않고 pytest 명령어를 사용
      • manage.py를 사용하지 않는 이유는 unittest를 임포트해서 TestCase 클래스의 서브 클래스로 선언할 필요가 없고, 단순하게 테스트 함수만 작성하는 것만으로 테스트를 작성할 수 있기 때문
      • 그리고 Fixture를 관리할 수 있는 편리함과 pytest의 다양한 플러그인도 사용할 수 있다는 장점이 있음

Pytest 사용 이유

  • django에는 기본 테스트 모듈이 내재 되어 있음
    • 그런데 왜 Pytest를 별도로 실행을 해야하는 걸까?
  • django 내장 테스트는 Boiler-Plate가 존재
    • 모든 테스트 set이 단일 클래스(django.test.TestCase)를 상속받아 운영
    • 실행이 순차적으로 진행이 되다 보니 규모가 커지면서 느려짐
      • 반면 pytest는 멀티 실행을 지원

Tip! Boiler-Plate(보일러플레이트)란?

  • 최소한의 변경으로 여러 곳에서 재사용되며 반복적으로 비슷한 형태를 띄는 코드를 말함
  • 어원: 보일러플레이트 코드의 어원은 신문사업에서 나왔음
    • 1890년대에 광고나 컬럼과 같이 계속 사용되는 텍스트 인쇄판은 부드러운 납 대신 강철로 찍기 시작했는데 이를 Boiler-Plate라고 불렀음
  • 코드 작성이 단순해짐
    • django
      • 모든 테스트가 class 상속으로 이루어짐
      • assert를 각 classmethod로 평가해야 함
    • pytest
      • function 단위로 테스트를 작성 할 수 있음
      • 단순 assert로 테스트를 평가할 수 있음
  • django다른 파이썬 프로젝트에도 도입이 가능
  • ④ 그 외 테스트 config를 파일로 지정해 놓을 수 있으며 fixture를 정해놓고 여러 곳에서 원하는 순서에 맞추어 실행시켜 볼 수 있음

설치 및 기본 설정

설치

$ pip install pytest-django

기본 설정

# pytest.ini - Project root folder
# 아래 설정을 잡아 주지 않을 경우, pytest 실행 시 에러 발생

[pytest]
DJANGO_SETTINGS_MODULE = project.settings
  • 테스트를 실행할 때 django 프로젝트의 설정을 사용하기 때문에 pytest.ini 파일에 위와 같이 명시하거나 --ds=project.settings 또는 DJANGO_SETTINGS_MODULE 환경변수를 설정해야 함
  • python path 관리
    • pytest-django는 기본적으로 프로젝트의 manage.py 파일을 찾아보고 그 디렉토리를 python path에 자동으로 추가함

실행

$ pytest test.py directory
  • pytest-djangomanage.py 또는 django-admin.py를 이용해 테스트를 실행하지 않고, 단독으로 pytest 명령어를 실행하는 방식을 사용
    • 위와 같이 실행 파라미터를 이용해 테스트를 실행할 대상 모듈과 디렉토리를 직접 지정할 수 있음

실행 옵션

  • pytest.ini에서 지정 가능
[pytest]
addopts = --reuse-db
  • —db-reuse

    • 테스트 시작 시 DB를 만들고 삭제하는 작업을 하지 않음
    • 소규모 테스트를 한다면 중요
  • —create-db

    • 테스트 시작시 DB를 새로 만듬
    • models 변경 사항을 반영 할 수 있음
    • 공식 문서에 따르면 default—db-reuse를 기본으로 설정해두고 DB 스키마 변경시에만 사용하는 것을 추천
  • —migration
    • models 스키마 변경이 있을 경우 migration을 진행
    • django DB 가 아닌 테스트용 DB에 적용
  • —no-migration
    • 스키마 변경을 무시

데이터베이스 활용

  • pytest-django는 테스트할 때 DB를 접근하는 것에 대해 보수적으로 다룸
    • 따라서 기본적으로 테스트 과정에서 DB에 접근하려고 한다면 실패하게 됨
  • 테스트하려는 대상에서 DB에 접근을 하려면, 반드시 정확하게 명시해야만 허용
    • DB가 필요한 테스트를 최소화하는 것도 좋은 선택
  • 테스트에서 DB 접근이 필요한 경우 pytset-djangopytest mark를 사용

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
  • 클래스와 모듈 단위로 mark를 설정할 경우 모든 테스트에 적용할 수 있음
import pytest

pytestmark = pytest.mark.django_db

@pytest.mark.django_db
class TestUsers:
    pytestmark = pytest.mark.django_db
    def test_my_user(self):
        me = User.objects.get(username='me')
        assert me.is_superuser

Transaction

  • django 자체에 TransactionTestCase 클래스는 트랜젝션을 통해 격리된 상태에서 테스트를 수행하게 하고, 테스트를 마치면 DB 초기화를 해줌
    • 하지만 이 상태에서 수행되는 테스트는 트랜젝션 중에 생성된 DB 데이터를 비우는 과정 때문에 매우 느리게 수행될 수 있음
    • 이와 같은 기능을 사용하려면 django_db markTransaction=True 파라미터를 전달
@pytest.mark.django_db(transaction=True)
def test_spam():
    pass

테스트 전용 데이터베이스

  • --reuse-db데이터베이스를 재사용하기 위한 실행 옵션이고, --create-db데이터베이스를 강제로 다시 생성하는 실행 옵션
  • 처음 테스트를 실행할 때 --reuse-db를 사용하면 새로운 테스트 전용 DB가 생성되는데 모든 테스트가 종료 되더라도 테스트 DB는 지워지지 않음
    • 그리고 다음 테스트를 실행할 때 동일하게 --reuse-db를 사용하면 이전 테스트 DB를 다시 사용하게 됨
    • 이 옵션은 적은 테스트를 실행할 때나 DB 테이블이 많은 경우 유용
  • --reuse-db 옵션을 기본 pytest.ini 옵션으로 지정하고, 스키마가 변경되었거나 했을 때 --create-db 옵션을 사용하는 것을 추천
[pytest]
addopts = --reuse-db
  • --nomigrations를 사용할 경우 django migrations와 모든 모델 클래스 검사를 위한 DB 생성을 수행하지 않음

Django Helper

Marker

  • pytest.marker를 이용해 테스트 함수나 클래스에 메타 데이터를 쉽게 설정할 수 있음
  • pytest.mark.django_db
    • 테스트 함수에서 DB 사용이 필요하다는 것을 나타냄
    • 모든 테스트는 각각의 DB 트랜젝션 안에서 수행되기 때문에 테스트가 종료되면 변경된 데이터도 함께 롤백
  • pytest.mark.urls
    • djangoURLCONF을 직접 지정할 수 있음
    • 예시: myapp.test_urls

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'

Tip! 추가 내용

다중 데이터베이스 설정

pytest-django 공식 문서 | Tests requiring multiple databases

  • 4.3 버전부터 지원

Tip! 추가 내용

Pytest 기본 명령어 및 옵션

실행 명령어

$ pytest
  • 디렉토리 내 test_*.py 또는 *_test.py 파일을 모두 실행
  • 만약 프로젝트 root 경로에서 특정 디렉토리 내부의 테스트 파일을 수행해야한다면, 아래와 같이 root 경로 기준 파일 위치를 입력하여 테스트 파일을 실행
$ python -m pytest {디렉터리명}/{테스트파일명}.py

k 옵션

  • 테스트 파일의 특정 테스트 함수만 실행할 경우 -k 옵션을 사용
    • 테스트 함수명은 무조건 test_로 시작해야만 테스트 함수로 인식
$ pytest {테스트파일명}.py -k {테스트함수명}

v 옵션

  • 기본 실행 명령어로는 Failed건만 F로 출력하고 Passed건은 .으로 표시
    • -v 옵션 추가 시에는 각 테스트 함수 실행 결과를 출력

vv 옵션

  • -v 옵션으로는 내용이 일부 생략될 수 있음
    • -vv 옵션을 추가하면 -v 옵션보다 verbosity level을 높여 더 자세한 테스트 실행 결과를 출력

s 옵션

  • 일반적으로 Failed건에 대한 stdout, stderr 메세지는 캡쳐됨
    • 해당 메세지 캡쳐 기능을 비활성화하고 싶을 때 -s 옵션을 사용
      • --capture=noshortcut이라고 볼 수 있음
  • 만약 Passed건에 대해서도 stdout, stderr 메세지를 캡쳐하고 싶다면 --capture=tee-sys 옵션을 사용하면 됨

r 옵션

  • short test summary info에 출력되는 테스트 결과에 대한 옵션
  • 테스트 결과 관련 옵션
    • N
      • none
    • f
      • failed
    • E
      • error
    • s
      • skipped
    • x
      • xfailed
    • X
      • xpassed
    • p
      • passed
    • P
      • passed with output
    • a
      • all except pP
    • A
      • all
  • -r 옵션 뒤에 붙이는 문자에 따라 short test summary info가 출력됨
    • 디폴트 옵션은 -rfEFailedError건이 출력됩니다.
  • 예시
    • -rA
      • 모든 테스트 결과 출력
    • -ra
      • Passed건 제외하고 모든 테스트 결과 출력
    • -fEs
      • Failed, Error, Skipped건 출력
    • -rN
      • 출력하지 않음

x 옵션

  • 테스트 결과에 Failed가 하나라도 발생할 때 테스트 수행을 멈추고 싶은 경우 -x 옵션을 사용

maxfail 옵션

  • 테스트 결과가 Failed N건 일 때 테스트 수행을 멈추고 싶은 경우 --maxfail=N 옵션을 사용

collect-only 옵션

  • 테스트 실제 수행이 아닌 테스트 실행 시 수행되는 모듈과 기능 및 매개 변수를 확인하고 싶은 경우 --collect-only 옵션을 사용

junit-xml 옵션

  • JUnit XML 형식의 테스트 결과 리포트를 출력하기 위해 --junit-xml={디렉터리명}/{xml리포트파일명}.xml 옵션을 사용
    • 이 옵션을 사용하여 pytest 실행 후 Jenkins에서 빌드 후 조치로 Publish JUnit test result report에서 해당 xml 파일 맵핑 설정을 해두면, Jenkins 빌드 결과 Test Result 메뉴에서 테스트 결과를 확인 가능

color 옵션

  • --color=yes 옵션을 사용하면 테스트 결과 출력 시, 색상을 입힐 수 있음
    • 이 옵션도 Jenkins Console Output에서 테스트 결과 확인 시, Passed/Failed을 강조된 색상으로 확인할 수 있어 유용

그 외 옵션

$ pytest -h
  • pytest -h 명령어로 그 외 옵션 확인 가능

설정 파일 (pytest.ini)

  • 옵션에 플러그인까지 사용하면 테스트 파일을 실행하기 위한 명령어가 점점 길어질 수 있는데, pytest.ini 파일에서 아래와 같이 테스트 파일로 인식할 파일 형태나 기본 실행할 명령어 옵션을 설정할 수 있음
[pytest]
python_files = test_*.py *_tests.py check_*.py
addopts = -vv --maxfail=10
profile
🌱 Backend-Dev | hwaya2828@gmail.com

0개의 댓글