안녕하세요. 이번 포스트에서는 Django UnitTest(유닛테스트)를 활용한 테스트를 소개해보겠습니다.
1. UnitTest(유닛테스트) 란?
먼저 유닛테스트에 대한 소개를 하겠습니다. 유닛테스트란 작성한 코드의 가장 작은 단위인 Method나 Function을 테스트하는 메소드 입니다. 그래서 작성한 로직을 테스트하는 유닛테스트 코드를 작성하여 테스트하게 됩니다. 유닛테스트는 아래 피라미드 구조도와 같이 가장 기본이 되는 테스트로 전체 테스트의 70%를 차지하는 모든 테스트의 기반과 같은 역할을 합니다.
2. UnitTest를 사용하는 이유(장점)
유닛테스트도 결국에는 Python 프로그램을 만들어야 하는 번거로움이 있는데 기존에 활용하던 Postman(포스트맨)이나 httpie를 활용하면 되지 않을까 하는 의문이 있을 수 있습니다. 코드를 작성하다 보면 확인도 하고 이런저런 비용이 들기 때문이죠. 그럼에도 불구하고 유닛테스트를 적극 활용해야 하는 이유를 말씀드리겠습니다.
2.1 API의 정확한 검증과 버그를 발견할 수 있다.
만드는 API가 10개 미만인 경우 포스트맨이나 httpie가 더 빠르게 확인이 가능할 수 있겠지만 실제 서비스할 Web이나 App에서 몇십 개가 되는 API를 하나씩 테스트하는 것은 실수도 생기고 부정확할 수밖에 없습니다. 그리고 정확한 순서를 지키기 때문에 예상치 못한 버그를 발견할 수 있습니다.
2.2 결국은 시간이 훨씬 단축된다.
직접 확인하는 것보다 시간이 훨씬 단축됩니다. 사람이 눈으로 확인하고 몇십 개의 API를 하나씩 검증하는 과정은 API가 10개 남짓일 때도 수분이 걸릴 수밖에 없습니다. 기능을 하나 더 추가하면 처음부터 다시 테스트를 해야 하는데 하나의 버전이 나올 때까지 수 없이 많은 테스트가 진행될 텐데 그때마다 하나씩 확인하는 것은 시간이 너무 아깝습니다. 그런데 유닛테스트는 모든 과정을 다 수행해도 수 초 밖에 걸리지 않습니다.
2.3 유지보수가 용이하다.
기존에 A라는 방식으로 구현되는 기능을 여러 가지 근거로 효과적이라는 것이 입증되어 B라는 방식으로 바꿀 때, 유닛테스트는 B라는 기능으로 바꿔주기만 하면 됩니다. 그리고 소요시간이 나오기 때문에 정확히 얼마나 개선 됐는지를 확인하기에 용이합니다.
이런 이유로 유닛테스트는 개발자의 실수를 막아주는 좋은 방패의 역할을 한다고 볼 수 있습니다.
3. Python UnitTest
python에는 unittest와 pytest라는 Library가 있습니다. Django에서 UnitTest를 지원해주는 라이브러리가 있기 때문에 UnitTest에 대해서만 다뤄보겠습니다.
먼저 유닛테스트를 진행하면서 자주 사용되는 용어를 소개하겠습니다.
- TestCase : unittest 프레임 워크의 테스트 조직의 기본 단위
- Fixture : 테스트를 진행할 때 필요한 테스트용 데이터 혹은 설정 등을 이야기한다. 주로 테스트 가 실행되기 전이나 후에 생긴다.
- assertion : unittest에서 테스트하는 부분이 제대로 됬는지를 확인하는 부분. Assertion이 실패하면 테스트도 실패한다.
python의 unittest라는 모듈을 사용해 테스트를 진행한다면 다음과 같은 절차를 따릅니다.
3.1 unittest Module import
import unittest
3.2 unittest.TestCase 클래스를 상속하는 테스트 클래스 생성
class MyCalcTest(unittest.TestCase):
3.3 테스트 클래스 안에 test_ 로 시작하는 테스트 메서드를 생성한다.
def test_add(self):
3.4 테스트 하는 함수나 메서드를 호출하고 결과를 self.assert~~ 와 같은 메서드로 비교한다.
assert method로는 assertEqual, assertTrue, assertFalse, assertRaises, assertRegex 등이 있습니다.
def test_add(self):
c = myCalc.add(20, 10)
self.assertEqual(c, 30)
4. UnitTest 작성해보기
Django에서 유닛테스트를 진행하기가 매우 수월합니다. 각 App을 만들 때, tests.py라는 파일이 생성됩니다. 또한, python의 UnitTest를 활용할 수 있는 Library가 Django에 있습니다. 저는 이를 활용해 유닛테스트를 구현해보겠습니다.
Django의 유닛테스트는 크게 세 파트(메서드)로 구성되어 있습니다.
test의 사전 작업을 준비하는 setUp, 테스트의 Clean Up 진행하는 tearDown, 테스트를 진행하는 test 함수로 구성되어 있습니다.
가장 쉬운 회원가입에 대한 예를 들어보겠습니다.
1 import json
2 import bcrypt
3
4 from django.test import (
5 TestCase,
6 Client
7 )
8
9 from .models import User
10
11 class SignUpTest(TestCase):
12 def setUp(self):
13 pass
14
15 def tearDown(self):
16 User.objects.all().delete()
17
18 def test_signup_post_success(self):
19 client = Client()
20
21 sign_up_info = {
22 "email" : "test01@gmail.com",
23 "password" : "12345678",
24 "phoneNumber" : "01012345678",
25 "name" : "홍길동"
26 }
27
28 response=client.post('/user/sign-up',json.dumps(sign_up_info),content_type='application/json')
29 self.assertEqual(response.status_code,201)
30
31 def test_signup_post_invalid_email(self):
32 client = Client()
33
34 sign_up_info = {
35 "email" : "test01gmail.com",
36 "password" : "12345678",
37 "phoneNumber" : "01012345678",
38 "name" : "홍길동"
39 }
40
41 response=client.post('/user/sign-up',json.dumps(sign_up_info),content_type='application/json')
42 self.assertEqual(response.status_code,400)
43 self.assertEqual(response.json(),{
44 "message" : "INVALID_EMAIL_OR_PASSWORD_OR_PHONE_NUMBER"
45 })
코드를 하나씩 살펴보겠습니다. setUp을 통해 테스트를 준비합니다. 회원가입의 경우 별 다른 생성 데이터가 필요하지 않기 때문에 pass 처리해주었습니다. 보통 여기서 테스트에 필요한 객체를 미리 생성해줍니다.
tearDown은 위에서 언급한 대로 테스트 과정 중에 생긴 데이터를 제거해주는 과정입니다. 예를 들어 User App에서 하나의 tests에는 회원가입뿐 아니라 로그인, 팔로우 등과 같은 여러 테스트가 진행됩니다. 각 테스트는 연관성이 있긴 하지만, 정확한 테스트 결과를 위해서 Clean Up을 반드시 해주고 다른 테스트를 진행하는 것이 좋습니다.
마지막으로 test입니다. 본 포스트 3.3의 내용과 같이 test_로 시작하는 함수를 작성해주어야 합니다. 각 클래스의 성공, 실패, 예외 케이스를 작성해주는 것이 필요합니다.
제가 작성한 함수의 경우, 성공 케이스 아래 유효하지 않은 email을 post 하게 하여 실패하도록 만들었습니다. 추가적으로 KEY_ERROR와 같은 예외처리 테스트 케이스도 작성하였습니다.(본 포스트에는 미기재)
여기까지가 Django의 Unit Test에 대한 전반적인 내용입니다. 후에 조금 더 깊이 다룰 수 있는 기회가 있다면 조금 더 깊이 있게 다뤄보겠습니다.
'Dev > Django' 카테고리의 다른 글
WebSite(Class101) Clone Project - Part2.구현 파트 정리(회원가입/로그인 소셜로그인) (0) | 2020.08.17 |
---|---|
WebSite(Class101) Clone Project - Part1.시작&나의 역할 (0) | 2020.08.09 |
WebSite(wiselyshave) Clone Project - Part.4 후기 (2) | 2020.08.03 |
WebSite(wiselyshave) Clone Project - Part.3 views.py Refactoring (0) | 2020.08.02 |
WebSite(wiselyshave) Clone Project - Part.2 Data Modeling & End Point Refactoring (0) | 2020.08.02 |