TIL no.78 - Django - Kakao Social Login (Back End)에서 구현한 Kakao Social Login View의 Test에 대해 포스팅하겠습니다.
Mocking하고자 하는 대상이 사용되는 곳을 경로로 잡아야 합니다.
이번 포스팅에서는 requests 모듈을 Mocking할 것이고
Mocking한 requests는 user앱의 views.py에서 사용될 것이므로
다음과 같이 경로를 설정해야합니다.
@patch(user.views.requests)
class Client(enforce_csrf_checks=False, json_encoder=DjangoJSONEncoder, **defaults)
instance를 생성할 때는 아무런 argument도 필요없습니다.
즉, c = Client()
로 Client instance를 생성할 수 있습니다.
**defaults
를 이용해 default header를 지정할 수 있습니다.
예를 들어, 다음과 같은 Client instance는
request마다 "User-Agent" header를 보냅니다.
c = Client(HTTP_USER_AGENT='Mozilla/5.0')
"HTTP_" prefix가 붙고
hyphen은 underscore로 해주고
UpperCase로 해줘야 하는데
이는 CGI specification를 따르기 때문입니다.
enforce_csrf_checks
,json_encoder
에 대한 설명은
Django docs Testing Tools를 참고해주세요.
get(path, data=None, follow=False, secure=False, **extra)
path
에 GET request를 보내고 response를 return합니다.
GET data payload는 dictionary 형태로 넣어줍니다.
>>> c = Client()
>>> c.get('/customers/details/', {'name': 'fred', 'age': 7})
위 요청은 다음과 같습니다.
/customers/details/?name=fred&age=7
**extra
keyword arguments parameter는
request에 담기는 header를 지정해줍니다.
>>> c = Client()
>>> c.get('/customers/details/', {'name': 'fred', 'age': 7},
... HTTP_X_REQUESTED_WITH='XMLHttpRequest')
위 요청은 /customers/details/
에 mapping된 view에
"HTTP_X_REQUESTED_WITH" header를 보냅니다.
또한 **extra
를 통해 보내지는 header들은 CGI specification를 따라야 합니다.
예를 들어, "Host"라는 header를 따라하기 위해서는 "HTTP_HOST"로 보내야합니다.
import jwt
import json
import unittest
from django.test import Client, TestCase
from user.models import User, SocialPlatform
from unittest.mock import patch, MagicMock
class KakaoSignInTest(TestCase): #Django의 TestCase를 사용
def setUp(self):
SocialPlatform.objects.create(platform_name='kakao')
def tearDown(self):
SocialPlatform.objects.get(platform_name='kakao').delete()
@patch('user.views.requests') #user 앱의 views.py에서 사용될 requests를 patch
def test_kakao_sign_in(self, mocked_request):
# 실제로 kakao API를 호출하지 않고
# kakao API의 응답을 Fake로 작성
class FakeResponse:
def json(self):
return {
"id" : 12345,
"properties" : {"nickname": "test_user"},
"kakao_account": {"email":"test@gmail.com"}
}
#test할 때, requests가 get 메서드로 받은 response는 FakeResponse의 instance
mocked_request.get = MagicMock(return_value = FakeResponse())
c = Client()
header = {'HTTP_Authorization':'fake_token.1234'}
# Client의 get method에 header담기
# **extra는 keyword arguments이나 header는 dict이므로 **을 붙여준다.
response = c.get('/user/kakao-login', content_type='applications/json', **header)
self.assertEqual(response.status_code, 200)
if __name__ == '__main__':
unittest.main()
코드에 관한 설명은 주석을 참고해주세요.
import jwt
import json
import requests
from django.views import View
from django.http import JsonResponse
from .models import SocialPlatform, User
from wemarpple.settings import SECRET_KEY
class KakaoLoginView(View):
def get(self, request):
try:
access_token = request.headers.get('Authorization', None)
url = 'https://kapi.kakao.com/v2/user/me'
headers = {
'Authorization': f'Bearer {access_token}',
'Content-type' : 'application/x-www-form-urlencoded; charset=utf-8'
}
#requests 자체를 mocking했으므로 실제로 kakao API를 호출하지 않음
kakao_response = requests.get(url, headers = headers) #FakeResponse의 instance를 받음
kakao_response = kakao_response.json() #FakeResponse instance의 json이라는 method의 return값을 받음
kakao = SocialPlatform.objects.get(platform_name = 'kakao')
if User.objects.filter(social_platform = kakao, platform_id = kakao_response['id']).exists():
user = User.objects.get(social_platform = kakao, platform_id = kakao_response['id'])
jwt_token = jwt.encode({'id':user.id}, SECRET_KEY, algorithm='HS256').decode('utf-8')
return JsonResponse(
{
'id' : f'{user.id}',
'name' : f'{user.name}',
'jwt_token': f'{jwt_token}',
},status = 200)
user = User.objects.create(
platform_id = kakao_response['id'],
name = kakao_response['properties']['nickname'],
social_platform = kakao,
email = kakao_response['kakao_account'].get('email', None)
)
jwt_token = jwt.encode({'id':user.id}, SECRET_KEY, algorithm='HS256').decode('utf-8')
return JsonResponse(
{
'id' : f'{user.id}',
'name' : f'{user.name}',
'jwt_token' : f'{jwt_token}',
}, status = 200)
except KeyError:
return JsonResponse({'message':'WRONG_KEY'}, status = 400)
test가 어떻게 돌아가는지는 주석을 참고해주세요.
매번 좋은 자료 감사합니다!