[DRF] DRF 게시글 작성 테스트코드

Yungsang Hwang·2022년 7월 13일
0

Django-Rest-Framework

목록 보기
6/15

거북이반 - drf 게시글 테스트코드

🚩8강

오늘의 순서

  • 회원정보 테스트코드
  • article의 테스트코드
  • setUpTestData
  • 파이썬
    • 클래스메소드
    • 스태틱메소드
  • article 테스트코드 2

🚩9강

  • simple-jwt를 사용하여 로그인을 시도한 경우에는 토큰을 담아서 테스트를 진행해야 한다
  • self.client 메서드로 테스트한 코드에서 나오는 값을 .data[response결과] 값으로 담아 변수로 초기화할 수 있음
# user.test
def test_get_user_data(self):
	access_token = self.client.post(reverse('token_obtain_pair'),self.data).data['access']
		response = self.client.get(
			path=reverse('user_view'),
			HTTP_AUTHORIZATION=f"Bearer {access_token}"
		)
		self.assertEqual(response.status_code, 200)
		# self.assertEqual(response.data['username'], self.data['username'])

🚩10강

  • 게시글 CRUD 테스트코드 작성준비
  • setUp 메서드와 setUpTestData 메서드의 차이
    • 셋업 메서드는 모든 메서드에 대해 실행될 때마다 한 번 실행되도록 되어 있다.
    • 변경되지 않는 데이터에 대해서(같은 유저 아이디와 같은 예시)는 매번 한 번씩 실행될 필요는 없기에 setUpTestData 메서드에 적어준다!
  • simple-jwt 토큰을 발행하고, 토큰을 넣은 요청과 함께 views.py에서 request.data로 로직을 구현하자
  • 생각해야 할 것은 POSTMAN으로 어떻게 찍을 것인지 생각해야 한다
# articles.tests.py

from django.test import TestCase
from django.urls import reverse
from rest_framework.test import APITestCase
from rest_framework import status
from users.models import User

# Create your tests here.
class ArticleCreateTest(APITestCase):
    
    def setUp(self):
        self.user_data = {'username': 'john', 'password':'johnpassword'}
        self.article_data = {'title':'some title','content':'some content'}
        self.user = User.objects.create_user('john','johnpassword')
        self.access_token = self.client.post(reverse('token_obtain_pair'),self.user_data).data['access']
# articles.views.py

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework import status
from rest_framework.response import Response
from articles.serializers import ArticleSerializer
from articles.models import Article as ArticleModel
# Create your views here.

class ArticleView(APIView):

    def get(self, request):
        articles = ArticleModel.objects.all()
        serializer = ArticleSerializer(articles, many=True).data
        return Response(serializer, status=status.HTTP_200_OK)
    
    def post(self, request):
        if not request.user.is_authenticated:
            return Response({"message":"로그인 해주세요"}, 401)
        
        serializer = ArticleSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response({"message": "글 작성 완료!!"})
        else:
            return Response({"message": f'${serializer.errors}'}, 400)

🚩11강

  • 클래스메서드(class method)

  • 예시) 사람 클래스와 메서드, 클래스메서드

    1. Person 클래스는 이름과 나이를 속성으로 받는 클래스 객체

    2. Person 클래스에는 display메서드fromBirthYear 클래스메서드를 가지고 있다

      class Person:
      	def __init__(self, name, age):
      		self.name = name
      		self.age = age
      
      	@classmethod
      	def fromBirthYear(cls, name, birthyear):
      		return cls(name, date.today().year - birthyear)
      
      	def display(self):
      		print(self.name + "'s age is" + str(self.age))
    3. 변수에 Person 클래스를 선언하고 name과 age를 인자 값으로 넣어 사용할 수 있게 된 즉석 개체, 인스턴스를 생성했다.

    4. 클래스 인스턴스가 된 변수 person은 클래스 내부의 함수, 메서드를 사용할 수 있게 되어 display() 의 형식으로 함수를 호출하여 이름과 나이가 어떻게 되는지 프린트 해 볼 수 있다.

      person = Person('Adam', 19)
      person.display()
    5. 변수 person1은 인스턴스를 생성하지 않은 상태에서 바로 Person 클래스의 클래스메서드인 fromBirthYear를 name, birthyear 인자를 넣고 바로 호출했다.

    6. 클래스메서드 fromBirthYear는 입력한 인자와 함께 상위 클래스인 Person을 cls 인자로 받았다

    7. return cls로 person1 변수로 초기화할 때 받았던 인자를 사용하여 클래스를 선언하는 형식에 맞게 값을 담아 인스턴스를 생성한다.

    8. 클래스메서드 return cls로 Person 클래스 인스턴스가 선언되었기 때문에, 클래스 내부의 함수인 display()를 변수 person1에서도 사용할 수 있게 되었다!

      person1 = Person.fromBirthYear('John', 1985)
      person1.display()

🙄메서드가 있는데 왜 굳이 클래스메서드를 사용해야 하는가?

  • 클래스메서드는 기존의 인스턴스를 선언하기 위한 인자 이외의 값을 받았을 때를 상정하여 작성할 수 있다고 생각한다!
  • 위의 예시로는 사람의 나이와 이름을 받아야 하지만, 출생년도를 인자 값으로 받는 상황에서는 인스턴스를 선언할 수 없다!
  • 따라서 클래스메서드를 통해서 다양한 케이스 처리를 해주는 것으로 생각하면 좋다!

🚩12강

  • 스태틱 메서드(static method)
  • 전역 클래스가 아닌 지역 클래스처럼 부모 클래스안에 스코프를 가둔다 (가독성 좋게하기 위해)
  • 하나의 클래스 내부에서 사용될 수 있을 함수를 엮어서 볼 수 있도록 일반 메서드 또한 스태틱메서드로 사용할 수 있다
@staticmethod
def isAdult(age):
	return age > 18
person1 = Person.fromBirthYear('황영상', 29) #클래스메서드의 활용
person1_is_adult = Person.isAdult(person1.age) #스태틱메서드의 활용

🚩13강

클래스메서드 setUpTestData와 메서드 setUp을 혼용해서 데이터 셋업하기

  • setUpTestData에는 모든 테스트가 하나의 공통된 데이터를 인자로 받을 때 사용한다
  • setUp 메서드는 client 메서드를 사용해서 테스트를 한 뒤 받아올 값과 같이 클래스 내부에서 사용하면 오류를 발생시키거나 한 번씩 각각 받아올 필요가 있을 때 사용한다

from django.test import TestCase
from django.urls import reverse
from rest_framework.test import APITestCase
from rest_framework import status
from users.models import User

# Create your tests here.
class ArticleCreateTest(APITestCase):
		@classmethod
    def setUpTestData(cls):
        cls.user_data = {'username': 'john', 'password':'johnpassword'}
        cls.article_data = {'title':'some title','content':'some content'}
        cls.user = User.objects.create_user('john','johnpassword')
        
    def setUp(self):
        self.access_token = self.client.post(reverse('token_obtain_pair'),self.user_data).data['access']

🚩14강

  • 테스트코드 함수명은 test_ 로 시작하는것이 좋다
def test_fail_if_not_logged_in(self):
        url =reverse("article_view")
        response = self.client.post(url, self.article_data)
        self.assertEqual(response.status_code, 401)
  • 특정 앱만을 테스트 하고 싶을 때는 뒤에 앱 이름을 같이 적어 테스트를 실행한다
python manage.py test articles

🚩15강

  • post 메서드 테스트
  • 리스폰스에 주소, 아티클 데이터, 토큰을 담아서 테스트코드를 실행하기
def test_create_article(self):
        response = self.client.post(
            path = reverse("article_view"),
            data = self.article_data,
            HTTP_AUTHORIZATION = f"Bearer {self.access_token}"
        )
        self.assertEqual(response.data['message'], "글 작성 완료!!")

🚩16강

  • 이미지를 포함한 게시글 작성 테스트
# 이미지 업로드
from django.test.client import MULTIPART_CONTENT, encode_multipart, BOUNDARY
from PIL import Image
import tempfile
  1. 임시 이미지파일 생성
    • temporary file 만들기
    • temp_file 이름 지정
    • get_temporary_image 로 임의의 이미지를 가져오기(내장기능)
    • 클래스 self 내부에 이미지 변수로 넣어두기
  2. 테스트코드에 멀티파트 데이터 선언
    • 클라이언트 메서드로 실행할 내부 인자에 주소, 인코딩 데이터, 컨텐트 타입, 토큰을 넣어 테스트 실행
    • assertEqual로 테스트 확인하기
def test_create_article_with_image(self):
	temp_file = tempfile.NamedTemporaryFile()
	temp_file.name = "image.png"
	image_file = get_temporary_image(temp_file)
	image_file.seek(0)
	self.article_data["image"] = image_file

	response = self.client.post(
		path=reverse("article_view"),
		data=encode_multipart(data=self.article_data),
		content_type=MULTIPART_CONTENT,
		HTTP_AUTORIZATION = f"Bearer {self.access_token}
	)
	self.assertEqual(response.data["message"], "글 작성 완료")
profile
하루종일 몽상가

0개의 댓글