본문 바로가기
Web/Django

[Django] Unit 테스트 코드 작성하기

by DUSTIN KANG 2024. 1. 10.

테스트 코드

그동안 PostMan으로 하나씩 API테스트의 수고로움을 덜기 위해 Django는 자동화된 테스트 도구들을 제공한다. 자동화된 테스트를 적용하면  직접 테스트하는 것보다 빠르고 정확하게 테스트를 진행할 수 있다. 테스트 코드 작성하는 것은 Django Shell을 사용하는 것과 다르지 않다. 그렇다면 테스트 코드를 한번 작성해보자. 테스트를 만들어야 하는 자세한 이유는 공식문서↗를 보면 자세히 나와있다.

테스트 코드는 테스트 주도 개발(TDD)라는 방법론이 있을 정도로 백엔드 개발에서 중요한 부분이다. 그러나 반드시는 아니다. 테스트 개발 방법론에 대한 설명은 해당 포스팅↗을 참고 하면 된다. 

 

만약 프로젝트의 단위가 큰 프로젝트라면 자동적으로 생성되는 `tests.py`를 수정할 필요 있다.

각 파일 단위를 테스트하거나 아니면 기능 단위로 나누어 테스트할 때 테스트 코드를 담을 폴더를 만들고 해당 폴더 내 `test_filename`형식의 파이썬 파일을 생성한다.

 

유닛 테스트

테스트 코드를 작성하기 위해 먼저 모듈 별로 테스트 클래스를 작성해야한다. 

먼저 시스템을 테스트할 때, 3가지로 나뉘게 되는데 유닛테스트, 통합테스트, UI테스트(EndtoEnd)가 있다. 그중 가장 쉽고 많은 부분을 차지 하는 것이 유닛테스트이다. 유닛테스트가장 작은 단위를 테스트하는 코드를 작성해 테스트하는 것을 말한다. 가장 작은 단위로 작성한다는 의미는 가장 작은 단위부터 정확히 동작하는지 확인해야한다는 의미이다. 유닛테스트 모듈은 파이썬에서도 `pytest`라는 패키지가 있지만 이 내용은 추후에 다뤄보도록하고 지금은 Django에서 제공하는 `unittest`를 구현하도록 하겠다.

 

Django에서는 다음과 같이 테스트를 하기전에 모듈을 불러와야 한다. 

from unittest import TestCase # Django에서 제공하는 유닛테스트 케이스
from rest_framework.test import APITestCase # DRF에서 제공하는 API 테스트 케이스

 

더보기
  • 단위 테스트 : 최소한의 기능 단위에 대한 테스트를 말한다.
  • 통합 테스트 : 몇개의 기능 단위를 묶어 테스트
  • 기능 테스트 : 사용자 스토리를 테스트, Selenium Test, UI 테스트

 테스트 클래스 작성하기(테스트 그룹)

불러온 테스트케이스 클래스를 상속받아 테스트 클래스를 만든다.

class LoginAPITests(TestCase):
    # Django Test 모듈
    ....
    

class LoginAPITests(APITestCase):
    # DRF Test 모듈
    ....

 

테스트 함수 작성하기(테스트 케이스)

테스트 클래스 내에 각 기능들을 테스트할 땐 테스트 메소드로 작성해야 한다. 테스트 메서드(함수)는 다음 가이드 라인을 따르는 게 좋다.

  • 테스트 함수는 `test_`를 접두어로 작성한다.
  • 테스트할 기능을 명확하게 표현하는 이름을 붙여야 한다.
  • 테스트 함수는 가능한 길고 서술적인 이름을 붙여야 한다.
  • 각 테스트 케이스는 독립적이어야한다. 반복해서 작성하는 부분이 없어야 한다. 

 

위 그림에서 볼 수 있듯이 `Client`는 중요한 역할을 하고 있다.

테스트 클라이언트는 가상의 웹 브라우저 역할을 하는 클래스로 뷰(View)를 테스트하는 데 사용한다.  주로 GET, POST등 요청을 해서 상태코드가 나오는 부분까지 확인한다. 

 

Fixture

테스트 함수를 작성하기전에 `setup` 함수를 사용하면 테스트 케이스들의 공통 자원에 대해 공유 혹은 재사용할 수 있다.

그런데, `setup` 말고도 `setupTestData`가 존재하는데 이 둘은 차이가 있다. 물론 `teardown`은 테스트 케이스가 종료한 후에 발생하는 메소드이다.

  • setup : 테스트 케이스가 실행되기 전에 test 데이터를 만들 수 있다.
  • setUpTestData : 테스트 그룹에 사용할 데이터를 만드는 데 사용한다.

Assertion

테스트 함수를 실행할 때 제대로 맞는 값이 실행되었는지 검증한다.

  • `assertEqual(a, b)` : A와 B가 맞는지 확인, 자세한 실행 결과를 보여줌.
  • `assert a == b` : A와 B가 맞는지 확인, 자세한 실행결과를 보여주지 않음(어디서 잘못됬는지) 
더보기

Django를 API 서버로만 이용할 때, 외부 사용자들에게 `header`를 통해 인증정보를 받아야 하는 경우도 있다.

이때, Test 코드에서도 이를 처리해야하는데 TestCase의 Client 요청을 보낼 때 아래 처럼 `**header` 형식으로 보내면 `HTTP_`를 떼고 header에 실어서 보내진다. 

from django.urls import reverse
from django.test import TestCase

class AircodeAPITests(TestCase):

	def test_schedule(self):
    	url = reverse("send_schedule")
    	header = {'HTTP_TOKEN':'afaew$@m#iow23!3@29fafewm@*'}
	    response = self.client.post(url, **header)

 

Test 코드 작성시 header 추가하기 - SSAMKO의 개발 이야기↗

테스트 실행하기

일반적으로 테스트 코드를 실행할 때는 커멘드에서 `manage.py`를 이용해 테스트를 실행할 수 있다.

# 모든 테스트를 찾아 실행하기
python manage.py test

# `app` 디렉토리 내의 모든 테스트 실행
python manage.py test app

# 하나의 테스트 케이스(클래스)만 실행
python manage.py test app.tests.TestClass

# 하나의 테스트 메소드만 실행
python manage.py test app.tests.TestClass.test_create_subject

 

pytest

파이썬에서 제공하는 강력한 테스트 라이브러리이다. 기존 유닛테스트보다 특별한 점은 반드시 클래스 형식으로 작성할 필요 없다는 것이다. 또한 `assert*()`를 사용할 필요 없다. 아주 간편한 라이브러리로 설치를 위해 다음과 같이 패키지를 설치하면 된다.

pip install pytest

# python -m pytest tests.py::TestClass::test_create_subject

 

이전에 유닛테스트를 통해 `assert*()`로 보고 해야지만 자세하게 보고 할 수 있었다. 그런데 pytest를 통해서 `assert*()` 없이 다음 처럼 보고 해도 자세하게 보고할 수 있다.

class TestMyAbs:
    def test_return_itself_with_positive_param(self):
        assert myabs(5) == 5
pytest랑 Github Actions의 차이는 무엇일까?
별거 없다. 테스트를 작성하는 것은 pytest이고 테스트를 간편하게 실행하는 것은 CI 도구라고 생각하면 편하다.

 

pytest는 다양한 플러그인들을 제공하는데 그중 `pytest-django`와 `pytest-selenium` fixture로 많이 테스트에 사용한다고 한다.

@pytest.fixture
def browser(live_server):
	# ...

 


☕️ 포스팅이 도움이 되었던 자료

오늘도 저의 포스트를 읽어주셔서 감사합니다.

설명이 부족하거나 이해하기 어렵거나 잘못된 부분이 있으면 부담없이 댓글로 남겨주시면 감사하겠습니다.