테스트 코드 작성

TDD 책 뒷 부분도 보는 것이 좋다.

ft/tests_post.py

import random

from django.contrib.auth import get_user_model
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APILiveServerTestCase

from snippets.models import Snippet

User = get_user_model()


class SnippetTest(APILiveServerTestCase):
    test_title = 'Test Snippet[{}] Title'
    test_code = 'print("Hello, world{}")'
    default_linenos = False
    default_language = 'python'
    default_style = 'friendly'

    test_username = 'test_username'
    test_password = 'test_password'

    def create_user(self):
        user = User.objects.create_user(
            username=self.test_username,
            password=self.test_password,
        )
        return user

    def create_snippet(self, num=1):
        """
        주어진 num 값 (기본값 1) 만큼의 snippet을 만든다.
        num이 1일 경우 생성 후 response를 리턴한다.
        :param num: 만들 Snippet 개수
        :return: num이 1일 경우 생성 요청 후 response
        """
        for i in range(num):
            test_title = self.test_title.format(i + 1)
            test_code = self.test_code.format('!' * (i + 1))
            url = reverse('snippet:list')
            data = {
                'title': test_title,
                'code': test_code,
            }
            response = self.client.post(url, data, format='json')
            if num == 1:
                return response

    def test_snippet_list(self):
        # Snippet 목록을 생성하기 전에 인증을 위해 유저를 생성하고 로그인
        self.create_user()
        self.client.login(username=self.test_username, password=self.test_password)

        # 1~20개 중 랜덤하게 Snippet 생성
        num = random.randrange(1, 20)
        self.create_snippet(num)

        # num 값과 일치하는 개수만큼 Snippet이 생성되었는지 확인
        self.assertEqual(Snippet.objects.count(), num)
        # values_list를 사용해서 필요한 항목만 가져올 경우
        for index, snippet_value_tuple in enumerate(Snippet.objects.values_list('title', 'code')):
            self.assertEqual(snippet_value_tuple[0], self.test_title.format(index + 1))
            self.assertEqual(snippet_value_tuple[1], self.test_code.format('!' * (index + 1)))

        # 쿼리가 매우 커서 일부만 사용할 경우
        for index, snippet in enumerate(Snippet.objects.all().iterator()):
            self.assertEqual(snippet.title, self.test_title.format(index + 1))
            self.assertEqual(snippet.code, self.test_code.format('!' * (index + 1)))

    def test_snippet_create(self):
        # Create 이전에 인증을 위해 유저를 생성하고 로그인
        self.create_user()
        self.client.login(username=self.test_username, password=self.test_password)

        # Snippet 생성 요청
        response = self.create_snippet()

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data.get('title'), self.test_title.format(1))
        self.assertEqual(response.data.get('code'), self.test_code.format('!'))
        self.assertEqual(response.data.get('linenos'), self.default_linenos)
        self.assertEqual(response.data.get('language'), self.default_language)
        self.assertEqual(response.data.get('style'), self.default_style)

Tutorial 4: Authentication & Permissions

Test 코드에 인증 추가

client.login(username='lauren', password='secret')

Postman 테스트

Authorization > Type: Basic Auth, Username, Password 입력 후 테스트 가능

BasicAuthentication

주의 : 프로덕션 환경에서 BasicAuthentication을 사용하는 경우 https를 통해서만 API를 사용해야 한다. BasicAuthentication은 헤더의 authorization 내용을 가로챌 경우, 복호화가 가능하다.

Setting the authentication scheme

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
    	# BasicAuthentication을 지우면,
        # Basic 인증을 사용하지 않게된다.
        # 실제 배포할 때는 사용하지 않는 것이 좋다.
      	'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    )
}

Tutorial 5: Relationships & Hyperlinked APIs

Adding pagination

Postman 테스트 > Params에 key = page, value = 2를 넣어 요청해본다.

Tutorial 6: ViewSets & Routers

Viewsets이라는 추상 클래스를 제공한다. (Vewset을 사용할지 GenericView를 사용할지는 선택사항이다.)

Refactoring to use ViewSets

ReadOnlyModelViewSet을 이용하면 listdetail 기능을 한꺼번에 처리할 수 있다.

ModelViewSetlistcreate, retrieve, update, destroy 기능을 자동으로 지원한다. 여기에 highlight 기능을 추가해줘야 한다.

class SnippetViewSet(viewsets.ModelViewSet):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                          IsOwnerOrReadOnly,)

    @detail_route(renderer_classes=[renderers.StaticHTMLRenderer])
    def highlight(self, request, *args, **kwargs):
        snippet = self.get_object()
        return Response(snippet.highlighted)

Using Routers

  • ViewSet은 URL name자동으로 생성한다.
  • DefaultRouter는 최상위 뷰를 자동으로 생성한다.

과제