내 두눈 밤이면 별이 되지, 나의 집은 뒷골목 달과 별이 뜨지요.
[ 체리필터, "낭만고양이" ]
더딘 작업속도에 점점 초조해져 갈 무렵, 사수님이 작업중이시던 기능 구현을 끝내고 합류하셨는데, 사수님도 DRF로 유닛 테스트를 만들어 본 적 없다고 하시면서 같이 공부하면서 만들어 보자고 하셨다. 또, 예제로 사용된 테스트 코드 샘플을 보여주시면서 이걸로 응용해 보자고도 하셨다.
class StreamPlatformTestCase(APITestCase):
def setUp(self):
self.user = User.objects.create_user(username='test', password='Password!')
self.access = AccessToken.for_user(self.user)
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access}')
def test_stream(self):
data = {
'name' : 'tving',
'about' : 'test',
'website' : 'http://tving.co.kr'
}
response = self.client.post('/watch/stream/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response = self.client.get('/watch/stream/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
이것은 Udemy 강의에서 사용된 학습자료를 바탕으로 만들어진 유닛 테스트인데, 'setUp'의 모양새는 내가 만든 것과 같았지만, response
부분이 달랐다.
def test_school_create(self):
data = {
'school_name' : '서울대'
}
response = self.client.post(reverse('/school/', data))
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
reverse
는 Django 에서도 사용이 가능한데, 그 역할은 다음과 같다.
초보몽키님의 블로그
그래서 DRF공식문서 예시에 나온 것처럼 url 과 basename
을 사용해보았지만, 어째선지 자꾸만 에러가 났다. 이것 때문에 거진 하루를 소비했지만, 결국 방법은 알아내지 못했고 한숨만 쉬던 중 사수님이 주신 예제로 바꾸어보았더니, 바로 통과가 되었다.
class SchoolTestCase(APITestCase):
def setUp(self):
User = get_user_model()
self.user = User.objects.create_user(username='test', password='Password!')
self.access = AccessToken.for_user(self.user)
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access}')
def test_school_viewset(self):
data ={
'school_name' : 'test',
}
response = self.client.post('/school/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
Fail 이 뜨지 않는게 오히려 불안하다는 말을 들은 적이 있는데, 당시에는 개발자 전용 개그인줄로만 알았다. 그런데 단박에 OK가 뜨자, 묘한 위화감이 들면서 '과연 이게 정말 맞는 테스트 코드여서 OK가 뜬 걸까?' 하는 의문이 자리잡았다. 하지만 일주일이 거의 다 지나가고 있었고, 다른 API에 대한 테스트도 작성해야 했기에, 의문은 잠시 접어두고 기본적인 CRUD에 대한 테스트를 완성했다.
class SchoolTestCase(APITestCase):
def setUp(self):
User = get_user_model()
self.user = User.objects.create_user(username='test', password='Password!')
self.access = AccessToken.for_user(self.user)
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access}')
def test_school_viewset(self):
data ={
'school_name' : 'test',
}
patch_data = {
'school_name' : 'TESTname'
}
response = self.client.post('/school/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(School.objects.count(), 1)
self.assertEqual(School.objects.get().school_name, 'test')
response = self.client.get('/school/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = self.client.patch('/school/', patch_data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = self.client.delete('/school/', format='json')
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
드디어. 일주일을 통으로 갈아넣으면서 테스트 코드 한 토막을 완성했다(일단은 그렇게 보였다). post
부분에 두 줄을 추가한 이유는 create
가 제대로 적용되었다면, 1개의 데이터가 카운팅 될 것이고, name
이 'test'인 데이터여야 할 것이기에 검증 차원에서 추가해보았다. 테스트 결과도 모두 통과가 되었고, 사수님도 OK사인을 보내셨다. 이제 남은 것은 추가 된 기능에 대한 테스트 인데, '검색'과 serializer
에서 추가로 찾은 결과값에 대한 테스트가 필요했다. 과연 내일안에 성공할 수 있을지.
assert
함수reverse
에 대해 찾아보다가 우연찮게 assert
함수에 대해 알게 되었다. 파이썬 함수인 assert
는 assertEqual
처럼 메소드 형식으로 사용하는 경우도 있지만, 특정한 의미로써 assert
만으로도 사용하기도 한다. 이에 대해서 다음과 같은 설명이 있다.
예외를 발생시키는 예외처리랑 비슷하지만, 예외처리는 에러가 발생했을때 어떤 처리를 하기위한 코드이고, 이 assert (가정 설정문)은 어떤 조건이 True임을 보증하기 위해서 사용하는 것 입니다.
오류를 발생시키는 raise와 비슷하지만 다릅니다. raise에 대한 자세한 설명이 필요하다면 [바로가기]
raise는 만약에 오류가 발생했을때 "except 와 함께 이렇게 처리해라" 는 뜻이고
assert는 이 조건이 참일때 코드는 내가 보장한다. 이 조건은 올바르다!
하지만 이 조건이 거짓이라는 것은 내가 보증하지 않은 동작이다. 그러니 AssertionError를 발생해라.이런 식의 흐름입니다.
출처: [개발자 지망생]
참초한 블로그에 예시들도 자세히 나와있으므로 참고하면 좋을 듯 하다.
추가로 유닛 테스트에서 자주 사용되는 8가지 정도의 assert--
함수 종류에 대해서도 간략히 알아보자.
이젠 바다로 떠날 거예요, 거미로 그물 쳐서 물고기 잡으러
[ 체리필터, "낭만고양이" ]