Unit Test

이지훈·2021년 7월 24일
0

TIL

목록 보기
32/33
post-thumbnail

단위 테스트는 애플리케이션을 구성하는 하나의 기능이 올바르게 동작하는지를 독립적으로 테스트하는 것으로, "어떤 기능이 실행되면 어떤 결과가 나온다" 정도로 테스트를 진행한다.

Django는 기본적으로 unittest 를 사용하기 때문에 TestCase 클래스와 테스트를 위한 Client() 객체를 사용해서 작성한다.

테스트를 작성하는 경우에는 다음을 준수하는 것이 좋다.

  1. 1개의 테스트 함수에 대해 assert를 최소화하기
  2. 1개의 테스트 함수는 1가지 메소드만 테스트하기

1. 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)

2. 실습

우선 로그인 과정에 적용해보도록 하겠습니다.

## models.py
class User(TimeStampModel): 
    kakao_id    = models.BigIntegerField(null=True)
    name        = models.CharField(max_length=200, null=True)
    profile_url = models.CharField(max_length=2000, null=True)
    email       = models.EmailField(unique=True)
    password    = models.CharField(max_length=200, null=True)

    class Meta: 
        db_table = 'users'
        
## views.py
class SigninView(View):
    def post(self, request):

        try:
            data = json.loads(request.body)

            if not User.objects.filter(email=data['email']).exists():
                return JsonResponse({"message" : "INVALID_USER"}, status=401)

            user = User.objects.get(email=data["email"])

            if not bcrypt.checkpw(data["password"].encode("utf-8"), user.password.encode("utf-8")):
                return JsonResponse({"message": "INVALID_USER"}, status=401)

            access_token = jwt.encode({"user_id": user.id}, SECRET_KEY, ALGORITHM)

            return JsonResponse({"message":"success","access_token": access_token}, status=200)

        except KeyError:
            return JsonResponse({"message": "KEY_ERROR"}, status=400)

이것을 적용해보자면....

class SigninViewTest(TestCase):
    def setUp(self):
        password    = 'wlaa1234!'

        User.objects.create(
            id          = 1,
            password    = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8'),
            email       = 'BrendanEich@gmail.com',
            name        = 'gimgimgim'
        )

        access_token= jwt.encode({"user_id": 1}, SECRET_KEY, ALGORITHM)

    def tearDown(self):
         User.objects.all().delete()

    # 로그인 검증
    def test_signinview_post_success(self):

        client = Client()
        user = {
            'email'    : 'BrendanEich@gmail.com',
            'password' : 'wlaa1234!'
        }

        response       = client.post('/users/signin', json.dumps(user), content_type="application/json")
        access_token   = response.json()['access_token']

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json(), 
            {
                'message'       : 'success',
                'access_token'  : access_token
            }
        )

    # keyerror
    def test_signinview_post_keyerror(self):

        client = Client()

        user = {
            'password' : 'wlaa1234!'
        }

        response = client.post('/users/signin', json.dumps(user), content_type="application/json")

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json(), 
            {
                "message" : "KEY_ERROR"
            }
        )

    # password error
    def test_signinview_post_not_password(self):

        client = Client()

        user = {
            'password'  : 'wlaa1232!',
            'email'     : 'BrendanEich@gmail.com',
        }

        response = client.post('/users/signin', json.dumps(user), content_type="application/json")

        self.assertEqual(response.status_code, 401)
        self.assertEqual(response.json(), 
            {
                "message" : "INVALID_USER"
            }
        )

    # email error
    def test_signinview_post_does_not_existed_email(self):

        client = Client()

        user = {
            'password'  : 'wlaa1234!',
            'email'     : 'Brendah@gmail.com',
        }

        response = client.post('/users/signin', json.dumps(user), content_type="application/json")

        self.assertEqual(response.status_code, 401)
        self.assertEqual(response.json(), 
            {
                "message" : "INVALID_USER"
            }
        )

다음과 같이 작성할 수 있다.

3. 개선점

  1. setUp() -> setUpTestData() 변경하기
  • 테스트에서 읽기 전용 데이터로만 사용하는 객체의 경우 class method인 setUpTestData를 사용하면 한번만 생성할 수 있게 되므로 테스트 처리 속도가 빨라진다고 한다.
    가급적이면 setUp에서 매번 객체를 생성하는 것을 지양해야겠다.

4. 어려웠던 점

  1. setUpTestData()으로 변경을 하려고 하니 트랜젝션 오류가 생기기도 했다.
  2. **header 의 사용방법을 이해하기 어려웠다.
  3. 페이크 토큰을 만들어서 테스트 코드에만 사용한다는 개념을 이해하기 어려웠다.
  4. APIRequestFactory나 rest_framework_api 적용을 하기 어려웠다.

로그인 과정을 몇번이나 했어서 쉬울 것이라 생각했는데, 작성해보니 너무 어려웠다.... 잠시 미루고 kakao 로그인을 하고나서 다시 작성해봐야겠다.

profile
꾸준하게 🐌

0개의 댓글