이 글은 런던이라는 개발자의 테스트코드 관련하여 주관적인 생각과 경험으로 가득차 있습니다.
테스트 코드는 개발자가 생성한 구현코드를 검증하는 또 다른 결과물이라고 생각합니다.
테스트코드를 유지보수하기 위해 또 많은 시간을 할애하는 건 잘못된 방향이며 이를 피하기 위해서 어느정도의 통일된 작성 스타일, 테스트 수행 방향을 잡아야 전반적으로 테스트코드 유지에 도움이 된다고 생각합니다.
지금까지 시행착오를 겪으면서 또한 특급 시니어개발자에게 받은 노하우??와 본인만의 단위함수 작성 스타일을 공유하고자 합니다.
이 튜토리얼을 이해하신 다면 장고뿐이 아닌 여러 다른 프레임워크에서도 적용 할 수 있으리랴 생각 됩니다. 본인은 React(typescript), Laravel(PHP)를 활용한 프로젝트에서도 일괄적으로 적용했던 스타일입니다.
이 글을 읽는 유저는 단위테스트 코드를 작성하면서 어떤 방향으로 작성을 해야할 지? 잘 모르는 개발자에 좋은 가이드가 되었으면 하는 바램입니다.
장고의 기본적인 동작을 아는 자
pytest 기본적으로 작성을 알고 있는자.
Mock 개념을 알고 있는 자
북마크를 출력하는 페이지를 만들자고 가정합시다. 우선 북마크 모델을 생성해야하고 해당 화면을 처리하는 View를 구현할 것입니다. 마지막 웹에서 접속할 수 있게 url를 추가 할 것입니다. 아래는 위의 내용을 단계별로 정리한 것입니다.
- 북마크 모델 생성
- 북마크 목록 View 구현
- 북마크 목록 출력 URL 추가
개발자는 크게 3가지의 기능을 구현 할 것입니다. 테스트코드 이와 역시 3가지 기능에 대한 단위테스트 코드를 작성합니다.
모델 생성 코드
컬럼이 title, url 두개가 존쟁하는 Bookmark 모델을 생성합니다.
class Bookmark(models.Model):
title = models.CharField('TITLE', max_length=100, blank=True)
url = models.URLField('URL', unique=True)
def __str__(self):
return self.title
모델 생성 단위테스트 코드
@pytest.mark.django_db
def test_create_bookmark():
"""
모델을 생성 할 수 있다.
:return:
"""
# Given
title: str = "TEST_title"
url: str = "test.com"
# When
bookmark: Bookmark = Bookmark()
bookmark.title = title
bookmark.url = url
bookmark.save()
# Then
bookmark.refresh_from_db()
assert title == bookmark.title
assert url == bookmark.url
assert title == bookmark.__str__()
View 코드
ListView 상속 받아서 Bookmark 모델을 출력해주는 뷰를 구현합니다.
from django.shortcuts import render
from django.views.generic import ListView, DetailView
from bookmark.models import Bookmark
# Create your views here.
class BookmarkLV(ListView):
model = Bookmark
뷰 단위 테스트 코드
from unittest.mock import MagicMock, Mock
import pytest
from django.template.response import TemplateResponse
from bookmark.models import Bookmark
from bookmark.views import BookmarkLV
def create_mock_bookmark():
"""
Bookmark 모델 더미데이터 생성
:return:
"""
bookmark: Bookmark = Bookmark()
bookmark.title = "test_title"
bookmark.url = "http://www.naaaa.com"
bookmark.save()
@pytest.mark.django_db
def test_BookmarkLV_get():
"""
BookmarkLV 뷰의 get() 테스트 할 수 있다.
:return:
"""
# Given
mock_request = Mock()
mock_kwargs = Mock()
mock_kwargs.get = MagicMock(return_value=True)
create_mock_bookmark()
# When
view = BookmarkLV()
view.request = mock_request
view.kwargs = mock_kwargs
result: TemplateResponse = view.get(mock_request)
# Then
assert result.status_code == 200
assert result.context_data["bookmark_list"].first().title == "test_title"
bookmars/ 라우터를 추가합니다.
urlpatterns = [
path('admin/', admin.site.urls),
path('bookmarks/', BookmarkLV.as_view(), name='index'),
]
라우터 단위 테스트 코드
주로 요청자의 권한, 그룹 및 인증 위주로 검증합니다.
from unittest.mock import MagicMock, Mock
import pytest
from django.template.response import TemplateResponse
from django.test import Client
from bookmark.models import Bookmark
from bookmark.views import BookmarkLV
def create_mock_bookmark() -> None:
"""
Bookmark 모델 더미데이터 생성
:return:
"""
bookmark: Bookmark = Bookmark()
bookmark.title = "test_title"
bookmark.url = "http://www.naaaa.com"
bookmark.save()
@pytest.mark.django_db
def test_route_BookmarkLV_GET() -> None:
"""
BookmarkLV 뷰의 url method GET 테스트 할 수 있다.
:return:
"""
# Given
create_mock_bookmark()
client = Client()
# When
response = client.get('/bookmarks/')
# Then
assert response.status_code == 200
assert response.context_data["bookmark_list"].first().title == "test_title"
@pytest.mark.django_db
def test_route_BookmarkLV_POST() -> None:
"""
BookmarkLV 뷰의 url method POST 테스트 할 수 있다.
:return:
"""
# Given
create_mock_bookmark()
client = Client()
# When
response = client.post('/bookmarks/')
# Then
assert response.status_code == 404
Given / When / Then
테스트코드 작성 시 Given/When/Then 포멧을 만들어서 채우는 형식으로 작성합니다.
관련된 설명은 아래 링크 참고
ref : https://brunch.co.kr/@springboot/292
비슷한 기능으로 폴더 구분합니다.
test_(클래스명)_(멤버함수).py
함수단위 테스트코드 작성은 초기에 익숙해지는데 많이 어렵습니다. 익숙해져 있다면 디자인패턴, 클린아키텍쳐, 클린코드에 이미 어느정도 익숙한 상황일 것입니다. 마지막으로 개발에 대한 시야가 방대해진 개발자가 되고 있을 것입니다.