본문 바로가기
wecode/TIL 정리

Django TIL - 1. Unit Test

by 왕거 2020. 8. 22.

Testing Pyramid

Google Test Automation Conference가 제안한 Testing Pyramid

  • 피라미드를 나누고 있는 각각의 요소가 특정 테스트 방법
  • E2E <- Integration <- Unit 순으로 테스트의 난이도가 올라간다.
  • 가장 아래의 Unit Test를 전체의 70%, Integration Test를 전체의 20%, E2E Test를 전체의 10%로 전체 테스트를 구성하는 것을 제안

Testing Methods

  • E2E(End to End) Testing / UI Testing
    • 웹 브라우저를 통해서 직점 웹 사이트에 접속해서 여러가지 기능들을 테스트하는 방법.
    • Manual(수동) Testing을 통해서 진행하든 자동화를 통해서 진행하든 굉장히 까다롭고 어렵다.
  • Integration Testin
    • 두개 이상의 클래스 또는 서브 시스템의 결합을 테스트하는 방법으로 예를 들면 장고 서버를 실행해서 데이터베이스 시스템과 연동을 테스트하는 것
    • Httpie나 Postman을 사용해서 테스트를 진행하는 편
  • Unit Testing
    • 가장 쉽고 효과가 좋은 테스트 방법
    • 시스템의 가장 작은 단위인 함수에 대한 테스트를 진행한다.
    • 장점
      • UI Testing, Integration Testing에 비해서 테스트에 소모되는 여러 비용이 저렴하다.
      • 스크립트를 통해서 각각의 테스트를 자동으로 수행할 수 있다.
      • 다른 테스트에 비해서 진행 속도도 매우 빠르다.
      • Unit Test 집합을 잘 구성해 놓으면 이후의 유지-보수에도 도움이 된다.
      • Regression Test를 쉽게 반복 수행할 수 있다.
        • Regression Test란 이전에 통과했던 Unit Test 집합을 사용해서 현재의 버그를 찾아내는 테스트 방법으로 이전에 통과했던 Unit Test가 잘 구성된 집합이었다면 추후에도 도움이 될 가능성이 크다.

Test의 일반 원칙

  • 각각의 테스트 유닛은 가장 작은 단위이 집중해야하며, 해당 기능이 정확하게 동작하는 지를 증명해야 한다.
  • 각각의 테스트 유닛은 독립적이어야 한다. 단독으로도 실행가능해야 하고, 테스트 슈트를 통해서도 실행되어야 한다.
    • 호출순서와 무관하게 작동해야 한다.
    • 기존의 데이터와는 무관한 완전히 새로운 데이터 세트로 테스트를 진행해야 하며, 결과는 반드시 지워야 한다.
  • 테스트가 빠르게 수행되도록 노력해야한다.
    • 불가피하게 무거운 데이터를 다뤄야 하는 테스트의 경우는 별도의 테스트 슈트(일련의 연속된 테스트)로 분리하여 관리한다.
  • 현재 사용중인 툴에 대해서 개별 테스트나 테스트 케이스에 대해서 어떻게 수행하는지에 대해서 알아두어야 한다.
  • 하루 작업의 시작과 끝에 풀 테스트 슈트(구성한 전체 테스트)를 진행하는 습관을 들이면 좋다.
    • 작업 전 문제가 없는 상태임을 확인하고, 작업 후에도 문제가 없을 확인하는 것
  • 공유 저장소로 소스를 Push하기 전에 자동적으로 풀 테스트를 진행하는 것이 좋다.
    • 해당되는 훅을 구현해 놓는게 좋다.
  • 일종의 책갈피로서 다음 작업까지 텀이 길어질 것 같다면 필요한 지점에 일부러 실패를 유발하는 테스트를 만들어 놓는 것도 좋은 활용법
  • 테스트 이름은 길고 서술적이어야 한다.
    • 테스트 이름은 직접 노출될 일이 없고, 실패할 때에만 노출이 되는데 이 때 표시되는 이름을 통해서 어떤 부분에 문제가 있는지 파악을 하기 때문
  • 잘 구성된 테스트 코드는 그 자체로서 개발 문서로 활용할 수 있다.
    • 테스트 코드의 구성을 통해서 전체적인 구성을 파악할 수 있다.
    • 어떤 부분이 까다로운지, 어떤 부분이 수정이 필요할지 등등에 대한 대략적인 정보를 습득할 수 있다.

Django 프로젝트에 Unit Test를 적용해보기

  • 현재 진행중인 t2tea.com 페이지 클로닝 프로젝트에서 사용중인 회원가입 및 로그인 기능에 Unit Test를 적용해보기로 했다.
######################################################
#	/__PROJECT_ROOT__/user/tests.py

import json
import unittest

from django.test import Client, TestCase

client = Client()

class UnitTest(TestCase):
    test_user_data = {
        'first_name'               : 'TEST1',
        'last_name'                : 'Mr1',
        'phone'                    : '010-1111-0101',
        'email'                    : 'MrTEST1@email.com',
        'password'                 : '1q2w3e4r5t6ASD',
        'birthdate'                : '2020-08-20',
        'is_newsletter_subscribed' : False
        }
    def test_JoinView(self):
        response = client.post('/user/join', json.dumps(self.test_user_data), content_type = 'application/json')
        self.assertEqual(response.status_code, 200)

    def test_LoginView(self):
        client.post('/user/join', json.dumps(self.test_user_data), content_type = 'application/json')
        test_login_data = {
            'email'    : 'MrTEST1@email.com',
            'password' : '1q2w3e4r5t6ASD'
        }

        response = client.post('/user/login', json.dumps(test_login_data), content_type = 'application/json')
        self.assertEqual(response.status_code, 200)
        
#############################################################
#	실행 결과

>>> python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 2 tests in 0.664s

OK
Destroying test database for alias 'default'...
  • tests.py 코드 분석
    • 일단 일련의 테스트 과정에서 사용할 데이터 세트를 미리 선언한 후에 테스트에 사용했다.
    • user App에 구현한 회원가입('/user/join') 및 로그인('/user/login') 엔드포인트에 대한 테스트를 진행했다.
    • 로그인 기능의 경우에는 회원가입이 선행되어야 진행이 가능하기 때문에 회원가입을 위한 POST를 1차적으로 진행해 주었다.
    • 전체 테스트 진행 결과는 OK지만, 테스트 케이스가 단순했기 때문에 더 보완할 필요가 있어보인다.
  • manage.py test
    • 풀 테스트 슈트를 진행하는 명령어로 전체 유닛 테스트를 진행한다.
    • 명령어 구성 ex) python manage.py test [user.tests.UnitTest.test_JoinView]
      • 대괄호로 감싼 부분에 대해서 공백일 경우에는 풀 테스트를 진행한다.
      • 대괄호 부분에 적을 수 있는 내용은 <APP_NAME> . <tests.py> . <TEST_CLASS> . <TEST_FUNCTION_NAME> 순으로 구성

Unit Test 에 대한 느낀점

  • 전에는 기능 테스트를 위해서 장고 서버를 실행 한 후에 Httpie를 사용하는 방법을 썼었는데, 그러다보니 의미없는 테스트 데이터가 계속해서 DB에 쌓이는 문제가 발생했었다.
  • Unit Test라는 방법이 더 간단하기도 하고, 적절한 테스트 방법이라는 것을 알게 되었으니 앞으로 자주 사용하는 습관을 가지도록 노력하자.