TIL

[삽질기록] Unit test 자동화하기 step2

비아 VIA 2021. 9. 11. 19:17

아래의 링크에 이어서 쓰는 글이다.

 

[삽질 기록] Unit test 자동화하기 step1

작은 규모의 회사이다보니 아직 unit test가 없다. 규모가 더 커지기 전에 만들기 시작하는게 좋은 것 같다는 의견은 항상 있었지만 막상 우선 순위가 있는 task들을 하다보면 unit test를 만들기가 쉽

jellyfishdeveloper.tistory.com

들어가기 전 TMI를 하자면

이 글은 step1을 쓸때만 해도 코드를 발전시키면서 여러개의 글을 업데이트하려고 했지만

회사가 9월까지만 운영하고 문을 닫게 되면서 추가적인 코드는 업데이트하지 않게 되었다.

슬프게도 정말 삽질기록이 되어버렸지만

삽질을 통해서 그래도 많이 배울 수 있는 것 같다!

 

지난 글의 마지막 부분에서 썼듯

새로운 목표는 프로젝트 전체의 end point의 response를 테스트를 자동화 하는 것이다.

예를 들어 

urlpatterns = [
	path('crrent-user/', general.current_user_view),
    path('languages/', general.language_view),
  ]

이런식의 url들이 있다면

1. 새로운 test file을 만들고

2. response를 받아오는 test class를 만드는 것까지만 우선 자동화하는 것이다.

class TestCurrentUserView(APITestCase):
 	print("CurrentUserView")
    url = "/current-user/"
 	get_response = APIClient().get(url)
 	patch_response = APIClient().patch(url)

    def test_status_code(self):
        self.assertEqual(self.get_response.status_code, 401)
        self.assertEqual(self.patch_response.status_code, 401)

여기까지는 성공을 했기 때문에(그리고 나서 들었던 회사 운영 중단 소식...또르르)

이 단계까지의 글을 써보려고 한다.

 

우선 커맨드로 특정 문자를 입력하면 파일이 생성되고 그 파일 안에 test class를 작성하는 거라서

command를 만드는 파일 안에 코드를 작성했다.

특정 앱 이름을 추가하면 그 앱에 해당하는 변수들을 셋팅해주도록 했다.

class Command(base.BaseCommand):
    help = 'Do unit testing'

    def add_arguments(self, parser):
        parser.add_argument('app_name', type=str)

    def handle(self, *args, **options):
        if options['app_name'] == 'app1':
            app_name = 'app1'
            app_url = app1_url.urlpatterns
            save_to = 'app1'

        elif options['app_name'] == 'app2':
            app_name = 'app2'
            app_url = app2_url.urlpatterns
            save_to = 'app2'
        else:
            print('Invalid app name')
            app_name = ''
            app_url = None
            save_to = ''

 

아래의 코드는 파일을 원하는 위치에 만들고

내가 원하는 내용(classes)를 추가하는 기능을 한다.

만든 파일을 test로 실행하는 것까지를 목표로 했다.

 # Make new file and write classes
 f = open(current_path + f'/unit_test/{save_to}/test_class.py', 'w')
 f.write(classes)

# run command
command = f'unit_test.{save_to}.test_class'
call_command("test", command)

 

이제 파일 안에 들어갈 테스트 클래스들을 만들어줘야 한다.

for loop로 url을 돌면서

url마다 테스트 클래스를 만들고자 했다.

파이썬 파일 가장 윗줄에 작성하는 import 부분을 먼저 넣어줬다.

from rest_framework.test import APITestCase, APIClient

 

class_name_list를 저장하는 이유는 하나의 url로 다양한 방법의 request를 보내는 경우(GET, POST, PATCH 등...)

test class 이름이 중복되어 생성되었기 때문에 이를 막고자

class_name_list에 class name을 저장해두고 같은 이름에는 뒤에 번호를 붙였기 때문이다.


classes = 'from rest_framework.test import APITestCase, APIClient\n' \
			'\n'\
            '\n'
            
class_name_list = []

 

어렵고 시간이 많이 걸렸던 부분은

url을 통해서 view 이름, method, permission 등을 찾아내는 작업이었다.

 

예를 들어 아래와 같은 urlpatterns에서 'current-url/', view의 이름(ex. current_user_view),

method(GET, PATCH, POST 등...), permission(AllowAny 등...)을 알아내려면 어떻게 해야 할까?

urlpatterns = [
	path('crrent-user/', general.current_user_view),
    path('languages/', general.language_view),
  ]

path(a, b)도 일종의 함수이기 때문에

path 속을 열심히 파고 print해가면서 찾을 수 있었다.

겨우 네줄이지만 정말이지 한참 걸렸던 것 같다.

for pattern in url_patterns:
    url = f'/pub/{app_name}/{patterns.pattern}'
    view = patterns.callback.__name__
    methods = patterns.callback.cls.http_method_names
    permission = patterns.callback.cls.permission_classes[0].__name__

클래스의 나머지 부분들은 정규표현식 등을 통해 채워나가서 어렵지 않게 구현할 수 있었다.

그래서 아래와 같은 형태의 permission에 따라 어떤 response가 있어야하는지 테스트 하는 부분까지 구현했다.

class TestCurrentUserView(APITestCase):
 	print("CurrentUserView")
    url = "/current-user/"
 	get_response = APIClient().get(url)
 	patch_response = APIClient().patch(url)

    def test_status_code(self):
        self.assertEqual(self.get_response.status_code, 401)
        self.assertEqual(self.patch_response.status_code, 401)

 

더 많은 기능을 추가하는 단계까지 가지는 못하게 되어서 아쉽지만

url을 통해서 method, permissoin, view 이름 등을 찾아보기 위해

path 함수를 공부하는 과정이 힘들어도 재미있었다!

 

 

'TIL' 카테고리의 다른 글

도커 볼륨  (0) 2021.09.24
도커 네트워크  (0) 2021.09.14
도커 레지스트리  (0) 2021.09.06
Week3 도커 이미지  (0) 2021.09.03
[삽질 기록] Unit test 자동화하기 step1  (1) 2021.08.27