Unittest

유동헌·2021년 5월 13일
0

유닛테스트란?

테스트 할 수 있는 가장 작은 단위를 테스트 하는 코드를 작성해 테스트 하는 것을 지칭

파이썬에는 유닛테스트라는 유닛테스트를 가능하게 하는 모듈이 이미 존재

파이썬에서의 유닛테스트 모듈 : 다양한 테스트를 자동화할 수 있는 기능이 포함되어 있는 표준 라이브러리

기본개념

유닛 테스트는 가상의 데이터베이스와 가상의 request를 통하여 코드의 작동 여부를 확인

가상의 데이터베이스에 한 기능의 테스트에 필요한 데이터를 생성하고, 테스트가 끝나면 지워주는 코드를 작성한다.(다른 기능 테스트의 테스트환경을 독립적으로 하기 위해서

기능이 정상적으로 작동했을 때의경우, 애러를 발생시키는 경우, 예외의 경우 등 독립적인 리턴값과 except값을 하나의 유닛으로 나누어 코드륵 작성한다.

Testcase 유닛테스트 프레임 워크의 테스트 조직의 기본 단위

Fixture 테스트를 진행할 때 필요한 테스트용 데이터 혹은 설정 등을 이야기 함. 주로 테스트가 실행되기 전이나 후에 생성 / 테스트가 실행되기 전에 테스트 환경이 예상된 상태에 있는지 확인하는 데 사용 / 테스트 전에 테이터베이스 테이블을 만들거나 테스트 후에 사용한 리소스를 정리하는데 사용

Assertion 유닛테스트에서 테스트 하는 부분이 제대로 되는지 확인하는 부분 assertion 실패 -> 유닛테스트 실패 / bool test 객체의 적합성, 적절한 예외 발생 등 다양한 점검을 할 수 있음

Test의 일반원칙

Unit test를 구현할때 지켜야 하는 일반적인 원칙들은 다음과 같습니다.

  • 테스트 유닛은 각 기능의 가장 작은 단위에 집중하여, 해당 기능이 정확히 동작하는지를 증명해야 합니다.
  • 각 테스트 유닛은 반드시 독립적이어야 합니다. 각 테스트는 혼자서도 실행 가능해야하고, 테스트 슈트로도 실행 가능해야 합니다. 이 때, 호출되는 순서와 무관하게 잘 동작해야 합니다. 이 규칙이 뜻하는 바, 새로운 데이터셋으로 각각의 테스트를 로딩해야 하고, 그 실행 결과는 꼭 삭제해야합니다. 보통 setUp() 과 tearDown() 메소드로 이런 작업을 합니다.
  • 테스트가 빠르게 돌 수 있도록 만들기 위해 노력해야 합니다. 테스트 하나가 실행하는데 몇 밀리세컨드 이상의 시간이 걸린다면, 개발 속도가 느려지거나 테스트가 충분히 자주 수행되지 못할 것입니다. 테스트에 필요한 데이터 구조가 너무 복잡하고, 테스트를 하려면 매번 이 복잡한 데이터를 불러와야 해서 테스트를 빠르게 만들 수 없는 경우도 있습니다. 이럴 때는 무거운 테스트는 따로 분리하여 별도의 테스트 슈트를 만들어 두고 스케쥴 작업을 걸어두면 됩니다. 그리고 그 외의 다른 모든 테스트는 필요한 만큼 자주 수행하면 됩니다.
  • 지금 사용하고 있는 툴이 개별 테스트나 테스트 케이스를 어떻게 수행하는지 배우셔야 합니다. 모듈 안에 들어있는 함수를 개발하고 있다면, 그 함수의 테스트를 자주, 가능하다면 코드를 저장할 때마다 자동으로 돌려야 합니다.
  • 그날의 코딩을 시작하기 전에 항상 풀 테스트 슈트를 돌려야 합니다. 끝난 후에도 마찬가지입니다. 이 작업은 당신이 다른 코드를 망가뜨리지 않았다는 더 큰 자신감을 심어줄 것입니다.
  • 모두가 공유하는 저장소에다가 코드를 집어넣기 전에 자동으로 모든 테스트를 수행하도록 하는 훅을 구현하는 하는 것이 좋습니다.
  • 지금 한창 개발 중인데 그만두고 잠시 다른 일을 해야한다면, 다음에 개발할 부분에다가 일부러 고장난 유닛 테스트를 작성하는 것도 좋은 생각입니다.
  • 코드를 디버깅할 때 가장 먼저 시작할 일은 버그를 찝어내는 새로운 테스트를 작성하는 것입니다. 이런 일이 언제나 가능한 것은 아니지만, 이런 버그 잡이 테스트들이야말로 당신의 프로젝트에서 가장 가치있는 코드 조각이 될 것입니다.
  • 테스트 함수에는 길고 서술적인 이름을 사용하셔야 합니다. 테스트에서의 스타일 안내서는 짧은 이름을 보다 선호하는 다른 일반적인 코드와는 조금 다릅니다. 테스트 함수는 절대 직접 호출되지 않기 때문입니다. 실제로 돌아가는 코드에서는 square() 라든가 심지어 sqr() 조차도 괜찮습니다. 하지만 테스트 코드에서는 test_square_of_number_2(), test_square_negative_number() 같은 이름을 붙여야 합니다. 이런 함수명들은 테스트가 실패할 때나 보입니다. 그러니 반드시 가능한 한 서술적인 이름을 붙여야 합니다.
  • 무언가 잘못되었거나 뜯어고쳐야만 할 경우, 괜찮은 코드에 테스트 셋이 있다면 당신이나 다른 유지보수 담당자들은 오류를 수정하거나 프로그램의 동작을 수정할 때 필시 그 테스트 슈트에 전적으로 의지할 것입니다.
  • 테스트 코드의 또다른 사용 방법은 새로운 개발자들을 위한 안내서로 쓰는 방법입니다. 이미 만들어져 있는 코드에서 작업해야할 경우, 관련 테스트 코드를 돌려보고 읽어보는 것이야말로 가장 좋은 시작점일 경우가 많습니다. 이렇게 테스트 코드를 돌려보면 어느 지점이 문제인지, 수정하기 어려운 곳은 어디일지, 막다른 골목은 어디일지를 발견하게 됩니다. 몇 가지 기능을 추가해야 한다면 가장 먼저 해야할 일은, 그 새로운 기능이 아직 돌아가지 않음을 확인할 수 있는 테스트를 붙여넣는 것입니다.

Unittest module의 사용

단순 실행만 확인
# app 안에 위치 / tests.py 
import unittest # 유닛 테스트 임포트 
from django.test import TestCase # [0] testcase 임포트

class JustTest(unittest.TestCase): # [1] 메서드 작성 / 유닛테스트, 테스트 케이스 상속
	def setUp(self): # [2]
    pass
  
  def tearDown(self): # [3]
    pass
  
  def test_runs(self): # test_ 이렇게 꼭 써줘야함. 그렇지 않으면 인식을 하지 못함. 
		'''단순 실행 여부 판단''' 
    
if __name__ == '__name__': # 유닛 테스트 실행 / 모든 테스트 케이스를 실행하는 코드
	unittest.main()
  
# python shell에서, python manage.py test users (내 경우는 users에서 테스트 중!) 실행!! 

# 특정 테스트 케이스만 실행하고자 하는 경우, 아래와 같이 테스트 할 클래스만 정의하여 실행할 수도 있음
#if __name__ == '__main__':
#     runner = unittest.TextTestRunner()
#     runner.run(unittest.makeSuite(CustomTests, 'test'))
# 또한 아래와 같은 다양한 형태의 단위 테스트를 지원합니다.
# 1) python -m unittest 모듈.클래스
# 2) python -m unittest 모듈.클래스.테스트메소드
# 3) python -m unittest 여러개 모듈, 클래스, 테스트메소드 나열
실행결과
❯ python manage.py test users                                                                                                           ─╯
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

# 단순 실행 성공

[0] 필요한 module import. TestCase는 유닛 테스트의 부모 클래스로, 유닛 테스트에 필요한 성격을 상속시켜줌. client는 가상의 메서드를 테스트의 대상인 view로 보내기 위해 사용

[1] view에서도 하나의 기능은 하나의 view로 구분이 되기 때문에 해당 기능의 테스트를 묶음을 클래스로 만들어줌

[2] 필요한 데이터를 생성하는 부분

[3] 삭제

회원가입 유닛테스트
import json
import unittest

from django.test import (
        TestCase, 
        Client
        )
from .models import User

class UserSignUpTest(unittest.TestCase):
    def test_setUp(self):
        pass

    def test_tearDown(self):
        User.objects.all().delete()

    def test_SignUpSuccess(self):
        client = Client() #[2]
        data = { # [1]
                'email'          : 'wewewe@gmail.com', 
                'password'       : '12341234', 
                'password_check' : '12341234', 
                'name'           : '김김김
          '
                }

        response = client.post('/users/users', json.dumps(data), content_type='application/json')
        self.assertEqual(response.status_code, 200)

    def test_EmailExist(self):
        client = Client()
        data = {
                'email' : 'wewewe@gmail.com',
                'password' : '12341234'
                }

        response = client.post('users/users', json.dumps(data), content_type='application/json') # [3]
        self.assertEqual(response.status_code, 404) # [4]

if __name__ == '__main__':
    unittest.main()
# Unittest.main()을 호출하며 unittest 실행. test를 원하는 함수는 unittest.testcase를 상속하여 클래스를 생성해주고 함수를 만들어 준다. test로 시작하지 않으면 테스트 하지 않는다. 

[1] 유닛 테스트가 동작하기 위해 유저가 실제로 입력해야 하는 데이터, json 형태로 바꾸기 위해 딕셔너리 객체로 저장

[2] 테스트의 해당이 되는 view에 요청을 보내기 위해 클라이언트를 설정해줌

[3] 앤드포인트 경로 지정. [1]에서 유저가 입력하는 데이터를 json으로 변환해 저장해주고, 데이터 형식을 json으로 헤더에 저장?

[4] 테스트 대상 view의 리턴 status와 내가 의도하여 만든 데스트 유닛의 리턴 status를 비교

json.dumps는 파이썬 객체를 json string으로 변환하는 역할을 하는 함수!!!

순서 정리

대략적인 순서

Unittest testcase 클래스를 상속하는 테스트 클래스 생성

테스트 클래스 안에 test_ 로 시작하는 테스트 메서드를 생성

테스트하는 함수나 메서드를 호출하고 결과를 self.assert와 같은 메서드로 비교함

각 중요 요소

assert~ 동등성 검사. 가장 많이 사용이 되는 메서드 같음. cf)assertEqual, assertTrue, assertFalse, assertRaises, assertRegex ...

setUp을 통해 테스트를 준비 / 보통 여기서 테스트에 필요한 객체를 미리 생성함

tearDown 테스트 과정 중에 생긴 데이터를 제거해주는 과정

user 앱에서 로그인 팔로우 등과 같은 여러 기능이 있고 이에 대한 테스트 역시 진행이 될텐데, 이 테스트를 할 때 생기는 데이터를 지우는 것

본 함수! 여기서만 test_ 이거 붙으면 됨!!

unittest 실행방법

python tests.py : 전체 실행

​ -v : 더 많은 로그를 제공하는 옵션

​ -f : 옵션 사용 시 에러 발생하면 바로 멈춤

python -m unittest tests(.py) : 기능 별로 테스트 가능

cf)

python -m unittest testcase

python -m unittest testcase.TestStringMethodes

python -m unittest testcase.TestStringMethodes.test_isupper : 이런 식으로 잘게 쪼개서 테스트 가능

의문
  • 각각 기능 별로 따로 정리를 해두면 좋을 것
  • tearDown을 맨 아래로??

참고 자료

https://velog.io/@hj8853/Django-Unit-Test

https://hiseon.me/python/python-unittest-example/

https://m.blog.naver.com/wideeyed/221841799910

profile
지뢰찾기 개발자

0개의 댓글