๊ฐ๋ฐ์ค์ธ project์ 'kakao ๊ณ์ ์ฐ๋ signin' ๊ธฐ๋ฅ์ด ์๋ค. ๊ทธ๋ฆฌ๊ณ ์ด View ์๋ kakao api์ ์ฐ๋ํ๋ ๋ก์ง์ด ๋ค์ด์๋ค. unit test ๋์์ผ๋ก test ์ฝ๋๋ฅผ ์์ฑํ๋ ค ํด๋ ๋ก์ง ์์ฒด๊ฐ ์ด๋ฏธ Integration test ๋ฒ์์ ์ํ๋๋ฐ ์ด๋ฅผ unit test ํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น?
'kakao๋ก ์์ฒญํด์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ ๊ณผ์ '์ ๊ฐ์์ฒ๋ฆฌ ํ ์๋ ์์๊น?
kakao ๊ณ์ ์ฐ๋ signin์ unit testํ๋๋ฐ ์์ด [์ฑ๊ณต, ์์ธ, ์คํจ] 3๊ฐ์ง test๋ฅผ ์งํํด์ผ ํ๋ค.
์ด๋ฅผ ์ํด ํด๋น ๊ธฐ๋ฅ์ ๋ก์ง์ ์๋์ ๊ฐ๋ค.Frontend๊ฐ kakao api๋ฅผ ์ด์ฉ, access_token์ ์ป์ด Backend๋ก signin ์์ฒญ์ ํจ๊ณ ๋ณด๋ธ๋ค. Backend๋ kakao๋ก access_token์ ๋ณด๋ด ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๋ฐ์ ์ฒ๋ฆฌ ํ๋ค.
[์์ธ, ์คํจ] ์ ๊ฒฝ์ฐ frontend๋ก๋ถํฐ ๊ฐ์์ผ๋ก ์๋ชป๋ access_token์ ๋ฐ๊ฑฐ๋ ์๋ชป๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ ๊ฒ์ผ๋ก Test๊ฐ ๊ฐ๋ฅํ๋ค.
[์ฑ๊ณต]์ ๊ฒฝ์ฐ ๊ฐ์์ผ๋ก kakao๋ก๋ถํฐ '์ฌ์ฉ์์ ๋ณด'๋ฅผ ๋ฐ์ ์ด๋ฅผ ์ฒ๋ฆฌํด์ผ ํ๋ค. ๊ทธ ์๊ธฐ๋ View๋ด๋ถ์์ kakao๋ก ์์ฒญํ๋ ๊ณผ์ ์ด ์คํ๋๋ฉด ์๋๋ค๋ ๊ฒ์ด๋ค.
์ด๋ป๊ฒ ์ค์ ๊ธฐ๋ฅ์ ๊ฐ์๊ธฐ๋ฅ์ผ๋ก ์ฐํํ ์ ์์๊น?์ด๋ฅผ ์ํด์ unittest module ์ค patch์ MagicMock ์ ๋ํด ์์์ผ ํ๋ค.
์งํ์ ์์ project์ ๋ฐ์ํ test code๋ฅผ ๋ณด์.# user/tests.py from django.test import TestCase, Client from unittest import patch, MagicMock class UserTest(TestCase): def setUp(self): client=Client() @patch('user.views.requests') def test_user_signin_with_kakao_success(self, mocked_request): class FakeResponse: def json(self): return { 'id' : 12345, 'kakao_account' : { 'profile' : { 'nickname':'test_user', 'thumbnail_image_url':'image_url_info', 'email':'test@example.com' } } } mocked_request.post = MagicMock(return_value=FakeResponse()) headers = {'HTTP_Authorization':'fake_token.1234'} response = self.client.post('/user/kakao_sign_in', content_type='application/json', **headers ) self.assertEqual(response.status_code, 200)
์๋ kakao ๊ณ์ ์ฐ๋ signin view์ ๋ํด success test code ์ด๋ค.
์ฐ์ 'patch' ๋ฅผ ์์๋ณด์.patch๋ test ๋์ ๋ชจ๋์์ ํด๋์ค๋ ๊ฐ์ฒด๋ฅผ mockingํ ์ ์๋๋ก ํ๋ decorater์ด๋ค.
์ code๋ฅผ ๋ณด๋ฉด user app์ views ๋ชจ๋์์ ์๋ requests ๋ชจ๋์ patch ๋์์ผ๋ก ํ๊ณ ์๋ค. ์ด๋ views๋ด์ requests๋ฅผ mocking ํ๋ค๋ ์๋ฏธ์ด๋ค. ํ์ธ์ ์ํด ์๋ user.views code๋ฅผ ๋ณด์.
# user/views.py class SignInWithKakaoView(View): @transaction.atomic def post(self, request): try: token = request.headers['Authorization'] profile = requests.post( "https://kapi.kakao.com/v2/user/me", headers = {'Authorization':f'Bearer {token}'}, data = 'property_keys=["kakao_account.email"]' ) profile = profile.json() user_id = profile.get('id', None) if not user_id: return JsonResponse({"message":"INVALID_TOKEN"}, status=400) kakao_account = profile.get('kakao_account') nickname = kakao_account['profile'].get('nickname', '') thumbnail = kakao_account['profile'].get('thumbnail_image_url', '') email = kakao_account.get('email', '') user = User.objects.update_or_create( email = email, defaults = { 'kakao_id' : str(user_id), 'nickname' : nickname, 'image_url' : thumbnail, 'updated_at': datetime.now(), 'email' : email, } ) token = generate_token(user[0].id) return JsonResponse({"message":"SUCCESS", "access_token":token}, status=200) except KeyError: return JsonResponse({"message":"KEY_ERROR"}, status=400)
ํด๋น View๋ฅผ ๋ณด๋ฉด kakao๋ก ์ฌ์ฉ์ ์ ๋ณด ์์ฒญ์ ์ํด requests ๋ชจ๋์ ์ฌ์ฉํ๊ณ ์๋ ๊ฒ์ ์ ์ ์๋ค.
์ฆ unit test์์๋ requests๋ฅผ patching ํจ์ผ๋ก์ ์ค์ ์์ฒญ๊ณผ์ ์ ๋ฌด์ํ๋๋ก ํ๋ค. ๊ทธ๋ฆฌ๊ณ kakao๋ก๋ถํฐ ๋ฐ์์ผํ๋'์ฌ์ฉ์์ ๋ณด'๋ MagicMock๊ณผ FakeResponse class๋ฅผ ํตํด mockingํ๋ค.
MagicMock์ Mockํจ์๋ฅผ ๋ง๋๋ ๋ชจ๋๋ก mock data๋ฅผ returnํ๋ ํจ์๋ผ ๋ณด๋ฉด ๋๋ค.
์์ธํ ์ค๋ช ์ Mock & MagicMock ์ ์ฐธ๊ณ .patching ํ requests์ post ํธ์ถ์ ๋ํ ์๋ต๊ฐ์ผ๋ก Mocking data์ ๋์ ํ๋ฉด Test์์๋ requests.post() ์ ๊ฒฐ๊ณผ๋ก mock data๊ฐ ๋์ค๊ฒ ๋๋ค.
์ด ๋ฐฉ๋ฒ์ ํตํด requests ์ ๋ํ patching, mocking ์ด ๊ฐ๋ฅํ๋ค.