Django UnitTest 코드 작성 튜토리얼(1) (실전편)
Tags: Tdd, Django, Unit Test

이 글은 런던이라는 개발자의 테스트코드 관련하여 주관적인 생각과 경험으로 가득차 있습니다.

테스트 코드는 개발자가 생성한 구현코드를 검증하는 또 다른 결과물이라고 생각합니다.

테스트코드를 유지보수하기 위해 또 많은 시간을 할애하는 건 잘못된 방향이며 이를 피하기 위해서 어느정도의 통일된 작성 스타일, 테스트 수행 방향을 잡아야 전반적으로 테스트코드 유지에 도움이 된다고 생각합니다.

지금까지 시행착오를 겪으면서 또한 특급 시니어개발자에게 받은 노하우??와 본인만의 단위함수 작성 스타일을 공유하고자 합니다.

이 튜토리얼을 이해하신 다면 장고뿐이 아닌 여러 다른 프레임워크에서도 적용 할 수 있으리랴 생각 됩니다. 본인은 React(typescript), Laravel(PHP)를 활용한 프로젝트에서도 일괄적으로 적용했던 스타일입니다.

이 글을 읽는 유저는 단위테스트 코드를 작성하면서 어떤 방향으로 작성을 해야할 지? 잘 모르는 개발자에 좋은 가이드가 되었으면 하는 바램입니다.

독자대상

장고의 기본적인 동작을 아는 자

pytest 기본적으로 작성을 알고 있는자.

Mock 개념을 알고 있는 자

시나리오

북마크를 출력하는 페이지를 만들자고 가정합시다. 우선 북마크 모델을 생성해야하고 해당 화면을 처리하는 View를 구현할 것입니다. 마지막 웹에서 접속할 수 있게 url를 추가 할 것입니다. 아래는 위의 내용을 단계별로 정리한 것입니다

1.북마크 모델 생성 2.북마크 목록 View 구현 3.북마크 목록 출력 URL 추가

개발자는 크게 3가지의 기능을 구현 할 것입니다. 테스트코드 이와 역시 3가지 기능에 대한 단위테스트 코드를 작성합니다.

1. 북마크 모델 생성

모델 생성 코드 컬럼이 title, url 두개가 존쟁하는 Bookmark 모델을 생성합니다.

models.py

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

모델생성을 검증하는 단위테스트 코드를 작성합니다.

/tests/models/test_bookmark.py

@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__()

2. 북마크 목록 View 구현

View 코드 ListView 상속 받아서 Bookmark 모델을 출력해주는 뷰를 구현합니다.

views.py

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

뷰를 검증하는 테스트코드를 작성합니다.

tests/views/test_BookmarkLV.py

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"

3. 북마크 목록 출력 URL 추가

bookmars/ 라우터를 추가합니다.

urlpatterns = [

path('admin/', admin.site.urls),



path('bookmarks/', BookmarkLV.as_view(), name='index'),

]

라우터 단위 테스트 코드를 추가합니다.

주로 요청자의 권한, 그룹 및 인증 위주로 검증합니다.

/tests/urls/test_urls_bookmarks.py

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

추천 도서

킨트백의 TDD TDD에 대한 오해와 진실TDD이야기- 당신이 TDD에 실패한 이유

마무리

함수단위 테스트코드 작성은 초기에 익숙해지는데 많이 어렵습니다. 익숙해져 있다면 디자인패턴, 클린아키텍쳐, 클린코드에 이미 어느정도 익숙한 상황일 것입니다. 마지막으로 개발에 대한 시야가 방대해진 개발자가 되고 있을 것입니다.