Unit Test with Python

김가람휘·2022년 3월 23일
0

Django

목록 보기
13/13

1. Unit Test with python

  • TestCase : unittest 프레임 워크의 테스트 조직의 기본 단위

  • Fixture : 테스트를 진행할 때 필요한 테스트용 데이터 혹은 설정 등을 이야기한다. 주로 테스트가 실행되기 전이나 후에 생긴다.

  • assertion : unittest에서 테스트 하는 부분이 제대로 됐는지를 확인하는 부분, assertion이 실패하면 테스트도 실패한다.


2. Unit Test 개발 가이드

  1. 먼저 unittest 모듈을 import 한다.
import unittest
  1. unittest.TestCase 클래스를 상속하는 테스트 클래스를 만든다.
class MyCalcTest(unittest.TestCase):
  1. 테스트 클래스 안에 test_로 시작하는 테스트 메소드를 생성한다.
def test_add(self)
  1. 테스트 메소드에서는 일반적으로 테스트하고자 하는 함수나 메소드를 호출하고 그 결과값을 self.assert()메소드를 사용하여 확인한다.
    -> assertEqual, assertTrue, assertRaises, assertRegex등 다양한 assert메소드들을 사용할 수 있다.
def test_add(self):
	c = myCalc.add(20, 10)
    self.assertEqual(c, 30)
  1. 테스트 클래스가 완성되었으면, unittest.main()을 호출하여 테스트를 실행시킨다.

2-1. Unit Test 예제

# mycalc.py
def add(a, b):
    return a + b

def substract(a, b):
    return a - b
# tests.py
import unittest
import mycalc

class MyCalcTest(unittest.TestCase):

    def test_add(self):
        c = mycalc.add(20, 10)
        self.assertEqual(c, 30)

    def test_substract(self):
        c = mycalc.substract(20, 10)
        self.assertEqual(c, 10)

if __name__ == '__main__':
    unittest.main()
> python -m unittest --v
test_add (test_my_calc.MyCalcTest) ... ok
test_substract (test_my_calc.MyCalcTest) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

2-2. Test Fixture

  • 테스트 시나리오에 따라 어떤 경우는 테스트 전에 테스트를 위한 사전 준비 작업을 할 필요가 있습니다. 또한 테스트가 끝난 후 테스트를 하기 위해 만든 리소스들을 정리(clean up)를 해야하는 경우도 있을 수 있습니다.

  • unittest는 이러한 사전 준비 작업을 위해 setUp()이라는 메소드를, clean up 처리를 위해 tearDown()이라는 메소드를 제공합니다.
    -> 이러한 기능들을 Test Fixture라 하며, Fixture는 각각의 테스트 메소드가 실행되기 전과 후에 매번 실행됩니다.

import unittest

class MyCalcTest(unittest.TestCase):
    def setUp(self):
        print("1. Executing the setUp method")
        self.fixture = { 'a' : 1 }

    def tearDown(self):
        print("3. Executing the tearDown method")
        self.fixture = None

    def test_fixture(self):
        print("2. Executing the test_fixture method")
        self.assertEqual(self.fixture['a'], 1)
> python -m unittest --v
test_fixture (test_my_calc.MyCalcTest) ... 
1. Executing the setUp method
2. Executing the text_fixture method
3. Executing the tearDown method
ok

3. TestCase 메소드

  • setUp() : test(method) 마다 불려서 test데이터셋을 만들어 준다.

  • setUpTestData() : TestCase(class) 한 개마다 초기 데이터 test 데이터를 만들어준다.
    -> 효율이 더 좋다.


4. Unit Test with Django

  • 앱 생성 시에 항상 함께 생성되는 tests.py을 통해 test를 진행합니다.

  • Django는 기본적으로 unittest를 사용하기 때문에 TestCase 클래스와 테스트를 위한 Client() 객체를 제공합니다.

    Client()
    Python class인 test client는 views를 테스트하도록 도와주는 가짜 웹 브라우저로 작동한다.
    -> Client를 사용하면 서버가 실행되지 않고 있는 상황에서도 프론트의 요청 없이 백엔드가 직접 views.py를 실험할 수 있다.
    -> GET, POST requests를 보내줄 수 있고, redirect나 status code도 확인할 수 있다.


5. Django tests.py

# planets/tests.py
from django.test     import TestCase, Client

from users.models    import User
from planets.models  import Accomodation, Planet, PlanetImage, PlanetTheme, Galaxy

class PlanetListTest(TestCase):
    @classmethod
    def setUpTestData(cls):	
        User.objects.create(
            id       = 1,
            name     = '박건규',
            password = '',
            email    = '박건규@박건규.com',
            kakao_id = 123456789111
        )

        Galaxy.objects.bulk_create([
            Galaxy(
                id   = 1,
                name = '우리은하'
            ),
            Galaxy(
                id   = 2,
                name = '가람우주'
            )
        ])

        PlanetTheme.objects.bulk_create([
            PlanetTheme(
                id   = 1,
                name = '제일 이쁘다'
            ),
            PlanetTheme(
                id   = 2,
                name = '가람테마'
            )
        ])
        
        ...
        

    def tearDown(self):
        User.objects.all().delete()
        Galaxy.objects.all().delete()
        PlanetTheme.objects.all().delete()
        Planet.objects.all().delete()
        PlanetImage.objects.all().delete()
        Accomodation.objects.all().delete()

    def test_success_planetlistview_get(self):
        client   = Client()
        response = client.get(  # 요청보내는 method
            '/planets'  # 요청보내는 urls 주소
        )
        self.assertEqual(response.status_code, 200)  # status_code 동일한지 확인
        self.assertEqual(response.json(), {  # json응답이 동일한지 확인
            'planets_list':[{
                'id'        : 1,
                'name'      : '이쁜행성',
                'thumbnail' : 'testurl/testurl/test',
                'galaxy'    : '우리은하',
                'theme'     : '제일 이쁘다',
                'image'     : ['testurl/testurl/test'],
                'accomodation_info' :[{
                    'min_of_people' : 2,
                    'max_of_people' : 4,
                    'price'         : '2500000.00'
                }]
            },
            {
                'id'        : 2,
                'name'      : '가람별2',
                'thumbnail' : 'http://urls2',
                'galaxy'    : '가람우주',
                'theme'     : '가람테마',
                'image'     : ['urls2'],
                'accomodation_info' :[{
                    'min_of_people' : 4,
                    'max_of_people' : 8,
                    'price'         : '200000.00'
                }]
            }]
        })

    def test_success_planetlistview_get_filter(self):
        client   = Client()
        response = client.get(
            '/planets?galaxy=2&theme=2&people=5'
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json(), {
            'planets_list':[{
                'id'        : 2,
                'name'      : '가람별2',
                'thumbnail' : 'http://urls2',
                'galaxy'    : '가람우주',
                'theme'     : '가람테마',
                'image'     : ['urls2'],
                'accomodation_info' :[{
                    'min_of_people' : 4,
                    'max_of_people' : 8,
                    'price'         : '200000.00'
                }]
            }]
        })
        
        ...

    def test_fail_planetlistview_get_invalid_date(self):
        client   = Client()
        response = client.get(
            '/planets?check-in=2022-03-19&check-out=2022-03-18'
        )
        self.assertEqual(response.status_code, 400)  # 에러코드 동일한지 확인
# bookings/tests.py
import jwt, json

from django.test import TestCase, Client

from my_settings     import SECRET_KEY, ALGORITHM
from planets.models  import Planet, Accomodation, Galaxy, PlanetTheme
from bookings.models import BookingStatus, Booking
from users.models    import User

class BookingTest(TestCase):
    @classmethod
    def setUpTestData(cls):	
        User.objects.create(
            id       = 1,
            name     = '가람',
            email    = 'kimkrh@naver.com',
            kakao_id = 1234
        )
        
        ...
        
    def tearDown(self):
        User.objects.all().delete()
        Galaxy.objects.all().delete()
        PlanetTheme.objects.all().delete()
        Planet.objects.all().delete()
        Accomodation.objects.all().delete()
        BookingStatus.objects.all().delete()
        Booking.objects.all().delete()

    def test_success_bookingview_post(self):  # test_success 또는 test_fail로 시작하는 것이 보기 좋다.
        token   = jwt.encode({'id':1}, SECRET_KEY, ALGORITHM)  # 임의로 토큰 생성 (Mock)
        client  = Client()
        headers = {'HTTP_Authorization':token}  # 헤더에 임의로 생성한 토큰 삽입 -> 로그인 데코레이터 통과
        booking = {  # body로 보낼 내용
            'booking_number'     : 1,
            'start_date'         : '2022-03-22',
            'end_date'           : '2022-03-23',
            'number-of-adults'   : 1,
            'number-of-children' : 1,
            'user_request'       : '테스트',
            'total_price'        : 10000,
            'planet_id'          : 1,
            'accomodation_id'    : 1
        }
        response = client.post(  # 요청보내는 method
            '/bookings',  # 요청보내는 urls 주소
            json.dumps(booking),  # 요청보낼때 보내는 body
            content_type='application/json',   # body content_type
            **headers  # 헤더확인
        )
        booking_number = Booking.objects.get(id=2).booking_number  # 생성된 uuid를 확인하기 위함
        self.assertEqual(response.status_code, 201)  # status_code 확인
        self.assertEqual(response.json(), {  # json 응답 확인
            'result':{
                'booking_id'     : 2,
                'booking_number' : str(booking_number)
            }
        })

0개의 댓글