TIL(41일차)

김규현·2022년 11월 1일
0

📝오늘 배운 것

💡DRF에서 테스트 코드로 기능을 테스트 하는 방법

테스트 케이스를 사용하기 이전에만 해도 구현한 기능을 테스트 하기 위해 프론트를 작성해서 프론트에서 확인하거나, 포스트맨을 통해 설계한 API에 요청을 보내는 방법으로 기능을 테스트 할 수 있었다.
포스트맨을 처음 접했을 때도 엄청 간편하게 구현한 기능을 테스트 할 수 있었지만 테스트 케이스를 사용해서 테스트 코드를 사용하면 다른 외부 툴을 사용할 필요 없이 빠르게 여러 기능들을 테스트 할 수 있다.

from django.test import TestCase
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase

class UserRegistrationAPIViewTestCase(APITestCase):
    def test_registration(self):
        url = reverse('user_view')
        user_data = {
            "email": "test@gmail.com",
            "password": "password"
        }
        response = self.client.post(url, user_data)
        self.assertEqual(response.status_code, 201)
    
    def test_login(self):
        url = reverse('token_obtain_pair')
        user_data = {
            "email": "test@gmail.com",
            "password": "password"
        }
        response = self.client.post(url, user_data)
        self.assertEqual(response.status_code, 200)

위 코드는 회원가입과 로그인을 테스트 하는 코드이다.
우선 url 변수에 담은 reverse는 요청을 보낼 url의 name 값으로 해당 url을 가져올 수 있다.
다음 user_data는 요청을 보낼 data를 담아 둔 것이고, response에 self.client.post로 위에서 정의한 url과 user의 data를 같이 보내고, self.assertEqual로 post 요청 후 status code가 201인지 비교하여 테스트한다.
(self.client는 APITestCase에서 생성된 인스턴스이다.)

아래 로그인도 같은 코드이나 위에서 만든 user data로 access 토큰을 발급받는 것 외 차이가 없다.
하지만 이렇게 테스트를 실행했을 때 에러가 발생한다.

이유는 모든 테스트 코드는 서로 의존하지 않고, 독립적이여야 한다는 원칙이 존재하고, db에 저장된 object로 테스트를 하는 것이 아니며 위의 회원가입 테스트가 끝나는 순간 초기화가 되어버리기 때문에 로그인 테스트에서는 테스트할 user가 없는 것이다.

따라서 setUp 또는 setUpTestData를 사용해 테스트에 적용할 data를 생성하고 이어서 독립적인 테스트 코드를 작성하여 구현한 기능을 테스트 할 수 있다.

📌 setUp

  • 모든 메소드에 대해 테스트를 하기 이전에 setUp를 매번 실행하여 테스트 데이터를 생성 후 메소드에 적용함
  • 사전 정의된 fixture(데이터셋)를 초기화하는 단계이며 모든 테스트 후에 실행된다.

📌setUpTestData

  • 변경이 되지 않는 데이터는 setUpTestData를 사용해서 TestCase가 실행되는 초기에 데이터 셋을 생성하여 모든 메소드에 적용함
class ArticleCreateTest(APITestCase):
    @classmethod
    def setUpTestData(cls):
        cls.user_data = {'email':'kim@gmail.com', 'password':'kimpassword'}
        cls.article_data = {'title':'some title', 'content':'some content'}
        cls.user = User.objects.create_user('kim@gmail.com', 'kimpassword')
        
    def setUp(self): #client는 
        self.access_token = self.client.post(reverse('token_obtain_pair'), self.user_data).data['access']

아래 코드는 faker 패키지로 setUpTestData에 랜덤한 값의 유저를 생성하여 해당 유저로 글을 작성하고, 불러오는 테스트이다.

url에서 get_absolute_url()을 사용하게 되면 article object가 실행될 때 get_absolute_url 메소드가 실행되고, 생성되는 object의 pk 값을 kwargs에 넣어서 reverse의 url로 전달한 후 해당 path의 reverse 값을 tests.py의 url로 돌아온다.

그 다음 response로 get 요청을 하여 나온 결과를 serializer에 적용하여 json 형식의 data로 변환하고 serializer.item()을 사용해 반복문을 돌려 response의 key에 해당하는 value 값이 일치한지 한 번에
확인할 수 있다.

class ArticleReadTest(APITestCase):
    @classmethod
    def setUpTestData(cls):
        cls.faker = Faker() 
        cls.articles = []
        for i in range(10):
            cls.user = User.objects.create_user(cls.faker.email(), cls.faker.word())
            cls.articles.append(Article.objects.create(user=cls.user, title=cls.faker.sentence(), content=cls.faker.text()))
    
    def test_get_article(self):
        for article in self.articles:
            url = article.get_absolute_url()
            response = self.client.get(url)
            serializer = serializers.ArticleSerializer(article).data
            for key, value in serializer.items():
                self.assertEqual(response.data[key], value)

models.py
	class Somemodel(models.Model)
    	#some field...
        
		def get_absolute_url(self):
        	return reverse("article_detail_view", kwargs={"article_id": self.id})

📌instance method

  • 첫 번째 매개 변수로 클래스의 인스턴스(self)가 넘어오게 되며 self를 통해 인스턴스 속성(attribute)에 접근하거나 다른 인스턴스 메서드를 호출할 수 있다.
  • self를 통해, 클래스 속성에 접근하거나 클래스 메서드를 호출할 수도 있다.

📌@classmethod

  • @classmethod 데코레이터를 사용해서 클래스에 메서드를 선언하면 해당 메서드는 클래스(class) 메서드가 되며, 첫번째 매개 변수로 클래스 인스턴스가 아닌 클래스 자체가 넘어오게 된다.
  • 매개 변수의 이름은 cls가 컨벤션이며 cls를 통해 클래스 속성(attribute)에 접근하거나, 클래스 메서드를 호출할 수 있다
  • 인스턴스 속성에 접근하거나 다른 인스턴스 메서드를 호출하는 것은 불가능하다.

📌@staticmethod

  • @staticmethod 데코레이터를 사용해서 클래스에 메서드를 선언하면 해당 메서드는 정적(static) 메서드가 되며, 첫번째 매개 변수가 할당되지 않는다.
  • 정적 메서드 내에서는 인스턴스/클래스 속성에 접근하거나, 인스턴스/클래스 메서드를 호출하는 것이 불가능하다.
profile
웹개발 회고록

0개의 댓글