TIL47 - Unit Test + 카카오 실습

Kiyong Lee·2021년 10월 21일
0

Django

목록 보기
27/35
post-custom-banner

Unit Test


1. Unit Test란?

내가 작성한 코드의 가장 작은 단위인 함수를 테스트하는 메소드를 말한다.

그래서 내가 작성한 로직을 테스트하는 유닛테스트 코드를 짜서 테스트하게 된다.


2. Test Pyramid

Google Test Automation Conference에서 제안된 테스트 피라미드로

시스템 테스트를 할 때 사진과 같은 3가지로 나뉜다

비중은 위에서부터 10, 20, 70% 이다.


2-1. E2E(End-To-End) / UI Test

간단하게 말하면, 프론트엔드에서 화면 띄어놓고 하는 테스트를 의미한다.

크롬 브라우저를 띄운 다음에, 내가 만든 검색페이지로 들어가서 검색을 해보고,
검색한 내용이 제대로 나오는지 브라우저상에서 직접 입력하여 테스트하는 방법이다.

3가지 테스트 방법 중, 가장 어렵고 까다롭다.

다만, 누구나 할 수 있어서 쉽다는 장점이 있으나, 비용이 많이 들고 부정확하며
시간이 오래 걸린다.

또한, 자동화하는 게 가장 까다롭다.


2-2. Integration Testing

간단하게 말하면, 백엔드에서 하는 테스트를 의미한다.

Postman, httpie등으로 직접 request를 호출하여 jsonresponse가 제대로 나오는지
확인한다.

E2E 다음으로 공수가 많이든다.


3. Unit Test의 장점


3-1. 저렴한 테스트 비용

UI TEST와 Integration Test는 사람이 직접 하지만,

유닛테스트는 사람이 스크립트로 한 번에 자동실행 하기 때문이다.


3-2. 빠른 실행 속도

자동실행 시켜주므로, 매우 빠른 속도이다.

그에 따라 하루에도 배포를 여러번 배포를 할 수 있어서, 개발 및 배포속도에
영향을 주기 때문에 개발할 때 최대한 활용하는 게 좋다


3-3. 유지보수 용이

이전에 통과했던 테스트 집합을 가지고 버그를 찾기 위해서 이전에 테스트 되었던
유닛테스트를 반복하는 걸 regression 테스트라고 하는데,

유닛테스트만 반복하면 된다는 점때문에, regression 테스트도 반복적으로 수행할 수 있다.


4. Test 원칙

  1. 테스트 유닛은 각 기능의 가장 작은 단위에 집중하여,
    해당 기능이 정확히 동작하는지 증명해야 한다.

  2. 각 테스트 유닛은 반드시 독립적이어야 한다.
    혼자서도 실행 잘 되어야 하고, 호출순서와 무관해야 한다.
    즉, 새로운 데이터셋으로 각 테스트를 로딩해야 하고, 그 실행결과는 삭제해야 한다.
    주로 setUp()과 tearDown()으로 작업을 한다

등등 여러가지가 있는데 직접 해보며 느끼는 게 좋을 것 같다


5. 실습 전 개념

TestCase
: unittest 프레임워크의 테스트 조직의 기본 단위

Fixture
: 테스트를 진행할 때 필요한 테스트용 데이터 혹은 설정 등의 이야기를 한다.
  테스트가 주로 실행되기 전이나 후에 생긴다

assertion
: 유닛테스트에서 테스트하는 부분이 제대로 됐는지 확인하는 부분
  실패하면 테스트도 실패

Mock
: 유닛테스트를 하다보면, 실제로 실행할 수 없는 코드들이 있다.
  대표적인 예로, 이번 프로젝트 때 내가 하게 된 소셜로그인이다.
  즉, 외부 API를 호출하는 코드들은 Mock을 이용한 테스트를 한다.
  프론트에서 사용하는 Mock Data의 의미가 이 뜻이었구나

함수명은 언제나 test_로 실행한다


6. 실습 ( Unit Test with Django )

from unittest.mock import MagicMock, patch
from django.test   import Client, TestCase


class UserTest(TestCase):
    @patch('users.views.requests')
    def test_kakao_signin_new_user_success(self, mocked_requests) :
        client = Client()

        class MockedResponse :
            def json(self) :
                return {
                    'id': 1955676444, 
                    'connected_at': '2021-10-19T03:58:18Z', 
                    'properties': 
                        {
                            'nickname': '용현'
                        }, 
                    'kakao_account': 
                        {
                            'profile_nickname_needs_agreement': False, 
                            'profile_image_needs_agreement': False, 
                            'profile': 
                            {
                                'nickname': '용현', 
                                'thumbnail_image_url': 'http://k.kakaocdn.net/dn/dpk9l1/btqmGhA2lKL/Oz0wDuJn1YV2DIn92f6DVK/img_110x110.jpg', 
                                'profile_image_url': 'http://k.kakaocdn.net/dn/dpk9l1/btqmGhA2lKL/Oz0wDuJn1YV2DIn92f6DVK/img_640x640.jpg', 
                                'is_default_image': True
                            }, 
                            'has_email': True, 
                            'email_needs_agreement': False, 
                            'is_email_valid': True, 
                            'is_email_verified': True, 
                            'email': 'dydgus0421@gmail.com'
                        }
                    }
        mocked_requests.get = MagicMock(return_value = MockedResponse())
        headers             = {'HTTP_Authorization' : "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.Iao6z7UkLoTMl3v4duh_30iwBS4ZgMBJeMHjHe8aXCMㅎㅎㅎ"}
        response            = client.post('/account/sign-in/kakao/callback', **headers)

        self.assertEqual(response.status_code, 201)

중요 포인트


1. class UserTest(TestCase)

항상 시작할 때, TestCase를 상속받고 시작해야 합니다.

처음에 def test_.. 부터 써서 계속 테스트가 안됐었습니다..


2. @patch(users.views.requests)

users/views.py의 requests 요청을 가로채라는 의미입니다.

headers  = {'Authorization' : f"Bearer {access_token}"}
URL      = "https://kapi.kakao.com/v2/user/me"
response = requests.get(URL, headers=headers)

제가 한 카카오 로그인의 코드 중 일부입니다.

프론트엔드로 부터 받은 토큰을 이용하여 카카오에 데이터를 요청할 때
response 줄에 있는 코드를 작성합니다.

외부API에 대한 테스트의 경우, 테스트를 할 때 마다 내가 작성한 코드가 맞는지
외부API에 요청을 하게 되는데, 당연히 이러면 안됩니다.

그래서 외부API를 끌어와 작성한 코드를 테스트할 때, patch함수를 통해
테스트를 할 동안만은 requests에 대한 결과를 나한테 달라고 요청하는 것입니다.


3. HTTP_Authorization

요청을 보낼 때, 헤더에 { Authorization : 토큰 }을 넣어서 토큰여부를 알 수 있습니다.

그래서 test.py를 작성할 때, Auth..라고 썼는데 테스트용은 달랐습니다.

HTTP_Authorization이라고 헤더의 키 값에 넣어줘야 테스트가 잘 돌아갑니다..


4. client.method

제가 생각했던 거는, 해당 url로 헤더에서 가져온 토큰에 대한 결과를 get으로
받아와서, response에 넣어서 assertEqual을 실행해라! 라고 이해했습니다.

그런데 client 뒤에 붙는 method의 경우, views.py에서 작성한 것과 같아야 합니다.

예를 들어, 카카오 로그인의 경우, def post()라고 설정했는데
client도 post라고 설정해야 한다는 것이죠

포스트맨을 사용하여 요청할 때와 같다고 생각하면 되겠다라고 이해했습니다.


5. assertEqual

테스트에 대한 기대되는 결과를 호출하는 메소드입니다.

내가 요청을 보냈을 때 응답코드인 response.status_code와
views.py에서 작성한 응답코드를 비교하는 것입니다.

그래서 응답코드가 같다, 틀리다로 내가 테스트를 잘했다는 것이 아니라
내가 틀린 결과를 유도했을 때, 응답코드가 틀리게 나왔다면 잘된거고
맞는 결과를 유도했을 때, 응답코드가 맞게 나왔다면 잘 된거다.

profile
ISTJ인 K-개발자
post-custom-banner

0개의 댓글