https://www.django-rest-framework.org/api-guide/testing/

 

Testing - Django REST framework

 

www.django-rest-framework.org

 

 

test code를 작성하는 이유?

1. 서버를 실행하고 DB에 데이터를 집어넣고 테스트 하는 등의 시간을 줄일 수 있다.

2. 테스트 코드를 먼저 작성하고 그에 맞춰 개발하는 방식으로 개발시간 단축, 깔끔한 코드 등의 이점을 얻을 수 있다 (TDD)

3. test를 진행할때 뿐 아니라 test 코드를 작성하는 과정에서도 미처 예상하지 못했던 오류를 찾을 수 있다.

4. test code 파일 자체가 api의 동작 방식과 결과에 대한 좋은 참고 문서가 될 수 있다.

5. 그냥 남들이 다 하길래

 

Django 및 DRF에서 test code를 작성하는 법

DRF는 unittest, TestCase,ApiReqeustFactory 등 여러 test code 작성을 위한 도구를 지원한다.

그 중 TestCase를 사용하기로 결정했다.

 

터미널에 python manage.py test를 입력하면

 

1. 장고는 프로그램속 TestCase를 상속받은 class를 찾아낸다.

2. 해당 클래스 내에서 setUp 과 "test"로 시작하는 메소드 함수를 찾아내 자동으로 실행한다.

3. 해당 함수 실행후 오류가 있는지 확인한 후 결과를 반환한다.

 

작성 예시

from django.test import TestCase

class MyTestCaseClass(TestCase): #TestCase 상속
    def setUp(self):
    	#setUp 데이터 생성
        
    def test_funtion(self):
        #테스트 하고 싶은 내용 입력

 

 

CocktailsURLTest class

from django.test import TestCase
from .models import *
import copy

test_cocktail_data = {
	"name" : "마이 타이",
    "base" : [
        {"name" : "다크 럼", "amount" : 30.0},
        {"name" : "골드 럼", "amount" : 30.0}
    ],
    "sub" : [
        {"name" : "오렌지 큐라소", "amount" : 15.0}
    ],
    "juice" : [
        {"name" : "라임 주스", "amount" : 30.0},
        {"name" : "오르쟈 시럽", "amount" : 15.0},
        {"name" : "심플 시럽", "amount" : 7.0}
    ],
    "other": [
        {"name" : "라임 필", "amount" : "1슬라이스"},
        {"name" : "민트 잎", "amount" : "3~4잎"}
    ],
    "recipe" : "위의 재료들을 ......... 완성.",
    "img_url": "https://t1.daumcdn.net/cfile/tistory/9923B0495D66434618",
    "glass" : "올드 패션드 글라스",
    "hashtag" : ["달달한","과일향이 나는"]
}

class CocktailsURLTest(TestCase):
    def setUp(self):
        Base.objects.create(name="다크 럼", alcohol_degree = 40.0)
        Base.objects.create(name="골드 럼", alcohol_degree = 40.0)
        Sub.objects.create(name = "오렌지 큐라소", alcohol_degree = 20.0)
        Juice.objects.create(name = "라임 주스")
        Juice.objects.create(name = "오르쟈 시럽")
        Juice.objects.create(name = "심플 시럽")
        Other.objects.create(name = "라임 필")
        Other.objects.create(name = "민트 잎")
        Glass.objects.create(name = "올드 패션드 글라스")
        HashTag.objects.create(name = "달달한")
        
    def test_api_cocktails_get(self):
        glass = Glass.objects.get(name = "올드 패션드 글라스")
        Cocktail.objects.create(name = "마이 타이", glass = glass)
        response = self.client.get('/cocktails')
        self.assertEqual(response.status_code,200)
        self.assertEqual(response.json()[0]["name"],"마이 타이")
        
    def test_api_cocktails_post(self):
        request_body = copy.deepcopy(test_cocktail_data)
        response = self.client.post('/cocktails',request_body,content_type = 'application/json')
        self.assertEqual(response.status_code,201)
        self.assertEqual(response.json()['name'],"마이 타이")

 

이 test code는 칵테일 레시피 사이트에서 레시피를 등록하고 조회하는 기능을 하는 /cocktails url을 테스트 하는 코드이다.

/cocktails url로 접속해 GET,POST method를 요청하면 DB에 있는 칵테일 레시피를 가져오거나, 새 레시피를 등록할 수 있다.

제대로 작동했을 경우 Django의 JsonResponse를 이용해 응답이 되돌아온다.

 

class CocktailsURLTest(TestCase):    
    def setUp(self):
        Base.objects.create(name="다크 럼", alcohol_degree = 40.0)
        Base.objects.create(name="골드 럼", alcohol_degree = 40.0)
        Sub.objects.create(name = "오렌지 큐라소", alcohol_degree = 20.0)
        Juice.objects.create(name = "라임 주스")
        Juice.objects.create(name = "오르쟈 시럽")
        Juice.objects.create(name = "심플 시럽")
        Other.objects.create(name = "라임 필")
        Other.objects.create(name = "민트 잎")
        Glass.objects.create(name = "올드 패션드 글라스")
        HashTag.objects.create(name = "달달한")

 

setUp 메소드 함수는 테스트가 시작되면 자동으로 실행된다.
앞으로 있을 test method 함수에서 사용될 데이터를 생성하는 용도로 사용한다.
필수는 아니며 생략 가능하다.

 

#views.py

@csrf_exempt
def cocktails(request):
    if request.method == 'GET': # 칵테일 레시피 전체 요청
        cocktails = Cocktail.objects.all()
        serializer = CocktailSerializer(cocktails, many=True)
        return JsonResponse(serializer.data,safe = False)
class CocktailsURLTest(TestCase):
	#.....
    
    def test_api_cocktails_get(self):
        glass = Glass.objects.get(name = "올드 패션드 글라스")
        Cocktail.objects.create(name = "마이 타이", glass = glass)
        response = self.client.get('/cocktails')
        self.assertEqual(response.status_code,200)
        self.assertEqual(response.json()[0]["name"],"마이 타이")

 

setUp 함수가 실행된 이후 "test" 라는 이름으로 시작하는 메소드 함수들이 실행된다.

self.client.get("my-url") 입력시 해당 url에 get 요청을 했을때 돌아오는 응답을 반환한다.

나는 JsonResponse 형태로 응답이 돌아오도록 views.py를 작성했다.

 

올바른 응답이 왔는지 확인하는 방법은 self.assert 함수를 이용하면 된다.

self.assertEqual은 함수 인자로 들어온 두 개의 값이 동일할 경우 테스트를 통과시키고 아닐경우 error를 반환한다.

assertEqual 뿐 아니라 assertTrue, assertFalse 등 여러가지 assert 함수가 존재하므로 상황에 맞게 사용하면 된다.

 

위 메소드 함수에선 assertEqual을 이용해

http status가 200인지, 돌아온 json의 "name" 항목이 "마이 타이"가 맞는지 확인한다.

 

 

TestCase 작성시 주의할 점

1. 파이썬은 TestCase를 상속받은 class에서 "test"로 시작하는 메소드함수만 테스트한다.

이걸 몰라서 처음에 왜 python manage.py test를 했는데 왜 아무것도 안일어나지 하고 당황했던 기억이 난다.

 

2. 메소드함수의 이름은 최대한 자세하게 지어야한다.

프로젝트의 크기가 커질수록 test code의 양도 굉장히 늘어난다.

또 test 실행 결과가 항상 잘 나오는 것은 아니다.

이때 수월한 디버깅을 위해선 함수 이름을 최대한 자세하게 지어야한다.

 

3. 하나의 메소드 함수에선 한개의 테스트만 진행한다.

마찬가지로 수월한 디버깅을 위해선 하나의 함수에서 하나의 경우의 수만 테스트해보는 것이 좋다.

 

 

 

 

 

 

 

 

 

 

 

 

 

+ Recent posts