ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Pytest] TDD를 해야하는 이유 (django TDD 적용하기)
    💻 프로그래밍/Django 2019. 12. 8. 21:54

    안녕하세요! 운동하는 개발자 Jay입니다.

    오늘은 TDD(Test Driven Development)를 해야 하는 이유와, Django api를 만들어서 간단하게 TDD를 적용해보도록 하겠습니다.

     

     

    1.  What is TDD(Test Driven Development)


    먼저 TDD가 뭔지에 대해서 간략하게나마 알아보겠습니다. 사실 처음 Test Driven Development라고 TDD를 단어로만 접했을 때는 단순히 테스트 케이스를 짜서 로직에 대한 테스트를 실행하는 걸로만 알았습니다. (물론 이것도 넓게는 TDD의 범위에 속한다고 생각합니다.)

    그래서 정확히 TDD의 정의를 찾아봤습니다.

     

    - 테스트 주도 개발(Test-driven development TDD): 매우 짧은 개발 사이클을 반복하는 소프트웨어 개발 프로세스 중 하나이다. 개발자는 먼저 요구사항을 검증하는 자동화된 테스트 케이스를 작성한다. 그런 후에, 그 테스트 케이스를 통과하기 위한 최소한의 코드를 생성한다. 마지막으로 작성한 코드를 표준에 맞도록 리팩토링한다. 이 기법을 개발했거나 '재발견' 한 것으로 인정되는 Kent Beck은 2003년에 TDD가 단순한 설계를 장려하고 자신감을 불어넣어준다고 말하였다. (from 위키백과)

     

    TDD는 단순히 테스트 케이스를 짜는 게 아닌 개발 프로세스입니다. 자 그럼 TDD가 왜 필요한지 한번 저의 개인적인(?) 생각과 회사에서 시니어 개발자 분의 말씀을 정리해 보겠습니다.

     

     

    2.  도대체 왜? why? TDD를 해야하는 거지?


    첫 번째.이봐 친구 날 믿어! 응 못 믿어.

    혹시 여러분은 여러분의 코드를 믿나요?? 물론 자신이 짠 코드를 믿고 개발을 계속 진행할 수도 있습니다. 하지만 개발을 하다 보면 실수로 오타(예를 들면 자동완성 기능으로 비슷한 이름의 함수가 호출된다거나....)가 나거나 버그를 만들어 낼 수도 있습니다.

    그리고 모든 개발자가 시니어가 아니며, 모든 개발자들이 천재는 아니잖아요? (물론 저도...)

    TDD는 여러분의 실수를 미리 파악하고, 버그를 보다 빠르게 잡을 수 있습니다.

     

    두 번째. QA는 품질 테스트? 버그테스트?

    보통 실서버 배포 전, 개발서버에서 QA를 하게 됩니다. 근데, 개발자가 실수로 버그를 만들어 놓고 그대로 QA를 하게 되면 불필요한 QA 리소스를 소비하게 됩니다. 개발자의 버그로 인해 테스트가 정상적으로 이루어지지 않기 때문입니다.

    그리고 다시 수정하고 배포하고 QA 하고, 개발자의 버그 때문에 불필요한 시간을 소비하게 되죠ㅎㅎ QA는 말 그대로 품질 테스트입니다! TDD는 최소한의 버그를 막고, QA 리소스를 낭비하지 않게 해 줍니다.

     

    세 번째.Side effect? 묻고 더블로가?!

    개발을 하다 보면 리팩토링을 하거나, 로직 수정, 추가 등의 작업이 추가적으로 있을 수 있습니다. 이런 작업들을 하다보면 다른 코드나 로직에 side effect가 생길 수가 있습니다. TDD는 이런 side effect를 방지해줍니다.

     

     

    3. pytest-django 알아보기


    python에서 TDD를 할 수 있는 라이브러리 중 pytest라는 라이브러리가 있습니다. 추가로 django에서 할 수 있는 플러그인으로 pytest-django가 있습니다. 

     

     pytest-django 플러그인 설치

    pip install pytest-django

     

     

     pytest 설정

    pytest.ini 파일을 manage.py와 같은 depth에 생성한 다음 환경변수 등을 정의한다.

    • --reuse-db: pytest 전용 database를 생성 한 뒤, 삭제하지 않고 재사용하는 옵션이다. 

    • --create-db : database 재 생성

    • --nomigrations : 장고 마이그레이션 관련과 DB에 관한 모든 모델에 대한 검사를 수행하지 않음

    * 참고: https://pytest-django.readthedocs.io/en/latest/configuring_django.html#pytest-ini-settings

     

     Django helper 

    pytest-django는 pytest에서 사용하는 mark 기능을 사용할 수 있습니다. mark는 test function에 필요한 meta data들을 쉽게 만들어 줍니다. (실제와 유사한) 아래와 같은 기능들이 있습니다.

     

    • pytest.mark.django_db - request database access

    • pytest.mark.urls - override the urlconf

    * 참고: https://pytest-django.readthedocs.io/en/latest/helpers.html#

     

     

     fixture

    • rf : django.test.RequestFactory (WSGIrequests 관련 데이터를 만들어준다)

    • client : django.test.Client (실제 url 호출하지 않고 fake로 호출하여 view를 테스트할 수 있게 해 준다.)

    * 참고: https://pytest-django.readthedocs.io/en/latest/helpers.html#fixtures


    4. TDD 실제로 적용해보기 with Django


    간단하게 user 정보를 가져오는 api를 만들고, pytest를 해보겠습니다.

     

    - views.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    from rest_framework import viewsets
    from rest_framework.response import Response
     
    from api.users.serializers import UserSerializer
    from apps.user.models import MyUser
     
     
    class UserInformationViewSet(viewsets.ModelViewSet):
        queryset = MyUser.objects.all()
        serializer_class = UserSerializer
     
        def list(self, request, *args, **kwargs):
            queryset = self.get_queryset()
            serializer = UserSerializer(queryset, many=True)
            return Response(serializer.data)
     
        def create(self, request, *args, **kwargs):
            pass
     
     

    MyUser 모델 viewset에서 get(list) 요청 시 user정보를 리턴하는 api를 생성합니다.

    - urls.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # -*- coding: utf-8 -*-
    from __future__ import unicode_literals
     
    from django.conf.urls import url
     
    from api.users.views import UserInformationViewSet
     
    app_name = 'users'
     
    urlpatterns = [
        url(r'^inform/$', UserInformationViewSet.as_view({'get''list'}), name='information'),
    ]
     
     

     

    - serializers.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from rest_framework import serializers
     
    from apps.user.models import MyUser
     
     
    class UserSerializer(serializers.ModelSerializer):
     
        class Meta:
            model = MyUser
            fields = ['id''username''email''age''company']
     
     

    위 api의 경로에 tests 폴더를 생성하고 __init__. py, test 할 파일을 생성합니다.

    - test_home.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    import json
    from datetime import datetime
     
    import pytest
    from rest_framework import status
     
    from rest_framework.reverse import reverse
     
    from apps.company.models import Company
    from apps.user.models import MyUser
     
     
    USER_FIELDS_COUNT = 5 # serializer user fields count
     
     
    @pytest.mark.urls(urls='conf.urls.urls')
    @pytest.mark.django_db
    def test_user_list(rf, client):
        url = reverse(viewname="users:information")
     
        # 테스트에 필요한 데이터를 생성해준다.
        establish = datetime.now()
        company = Company.objects.create(
            name='제이의 코드팩토리',
            date_establish=establish
        )
     
        MyUser.objects.create(
            age=29,
            company=company,
            username="jay_ji"
        )
        response = client.get(url)
     
        assert response.status_code == status.HTTP_200_OK
        data = json.loads(response.content)
        assert len(data[0]) == USER_FIELDS_COUNT
     
     
     

    간단하게 코드를 설명하자면, User 모델을 생성하고 url : 'users:information' 을 호출합니다.
    그다음 응답 코드(200)를 체크하고, response data의 필드 개수를 체크합니다.

    - 테스트 성공

    - 테스트 실패

     

    5. 마치며..


    pytest-django 기본적인 내용과 간단한 예제로 pytest를 사용하는 방법을 알아봤습니다.

    TDD는 그냥 개발하는 거에 비해 초반 개발 리소스가 더 많이 사용되게 되지만, 이후에 유지보수 등을 생각하면 버그, 사이드 이팩트 등의 가능성을 많이 줄일 수 있습니다.

    특히 저 같은 신입 개발자들에게 실수할 수 있는 가능성을 줄여주고, 많은 개발자들이 협업하면서 생길 수 있는 문제들에 대해서 어느 정도 방어적인 프로세스를 갖출 수 있는 것 같습니다.(예를 들어 버그 있는 코드를 실수로 배포한다던지... 이럴일은 거의 없겠지만요 ㅎㅎ)

    이상입니다. 좋은 하루 보내세요~:D

     

     

     

    댓글

운동하는 개발자 JAY-JI