Django UnitTest 코드 작성 튜토리얼(1) (실전편)

런던행·2020년 11월 5일
0

TDD in Django

목록 보기
1/2
post-thumbnail

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

테스트 코드는 개발자가 생성한 구현코드를 검증하는 또 다른 결과물이라고 생각합니다.
테스트코드를 유지보수하기 위해 또 많은 시간을 할애하는 건 잘못된 방향이며 이를 피하기 위해서 어느정도의 통일된 작성 스타일, 테스트 수행 방향을 잡아야 전반적으로 테스트코드 유지에 도움이 된다고 생각합니다.
지금까지 시행착오를 겪으면서 또한 특급 시니어개발자에게 받은 노하우??와 본인만의 단위함수 작성 스타일을 공유하고자 합니다.
이 튜토리얼을 이해하신 다면 장고뿐이 아닌 여러 다른 프레임워크에서도 적용 할 수 있으리랴 생각 됩니다. 본인은 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

추천 도서

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

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

profile
unit test, tdd, bdd, laravel, django, android native, vuejs, react, embedded linux, typescript

0개의 댓글