들어가기

이제부터 Django 프로젝트에 테스트를 기본적으로 하려고 한다. (이전부터 해야지~ 하고 있었다)

TDD
Test Driven Development. 테스트 주도 개발이라는 뜻으로, 테스트를 해가면서 개발을 하는 소프트웨어 개발 프로세스이다

테스트 파일 분리

Django App을 만들면 자동으로 tests.py 파일이 생성된다. 기본적으로 Django는 이 파일을 읽기 때문에 여기에 작성해도 되고, 테스트 파일을 나누고 싶다면 아래처럼 하면 된다

나는 test란 폴더를 만들어 테스팅 코드를 분리할 것이다
├── test
│ │ ├── __ init __.py
│ │ ├── test_account.py
│ │ └── test_pet.py
│ ├─ tests.py

tests.py

from .test import *

test/__ init __.py

from .test_account import *
from .test_pet import *

이런식으로 tests.py에서 모든 테스트 코드를 부를 수 있게 설정하면된다

테스트

기본

test/test_account.py

from django.test import TestCase

class AccountTest(TestCase):
  # 로그인 테스트
  def test_login(self):
    x = 1
    y = 2
    self.assertEqual(x+y,3) # x+y == 3 ?

❗️함수 앞에 test 를 붙여주지 않으면 테스트 코드로 인식하지 않아서, 테스트에서 제외되니 알아두자

이런식으로 테스팅이 진행된다. 지금은 프로젝트 기능이랑 상관없이 x+y가 3인지 확인하는 테스트가 되었다

API 테스트

이제 진짜 테스트에 사용할 API 테스트 코드를 작성해보자

실제로 URI 로 요청이 왔을때 처리하는 방법이다

email = "ghost@petgarden.com"
password = "password!!1"

test/test_account.py

from django.test import TestCase
from django.test import Client
from django.contrib.auth.models import User

class AccountTest(TestCase):
  # 로그인 테스트
  def test_login(self):
    client = Client()
    user = User.objects.create_user(username=email,password=password)
    response = client.post('/login',{'username':email, 'password':password})
    self.assertEqual(response.status_code, 200) 

간단한 로그인 테스트이다. client 를 이용하면 가상으로 API 요청을 할 수 있다

class AccountTest(TestCase):
  # 로그인 테스트
  def test_login(self):
    client = Client()
    user = User.objects.create_user(username=email,password=password)
    client.login(username=email,password=password)  # 가상 로그인
    response = client.post('/logout')
    self.assertEqual(response.status_code, 200) 

위처럼 가상으로 로그인 한것 처럼 요청도 가능하다

아마 다른 좋은 테스트 방법도 있을 것이다. Django를 접한지 얼마 되지 않아서 더 좋은 방법을 알게되면 꼭 글을 수정하겠다

이미지 테스트

몇 시간을 잡아먹은 테스트 이슈였다... Django 모델 중에 ImageField가 있는데 이것을 테스트 하기가 여간 까다로운게 아니였다 (나만 그런걸수도...)

여러 방법을 써봤지만 form.is_valid() 에서 걸려서 결국 파일을 따로 업로드 해야 한다는 것을 깨달았다

test/test_pet.py

from django.test import TestCase, override_settings
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import Client

email = "ghost@petgarden.com"
password = "password!!1"
# 이미지 기본 소스
small_gif = (
    b'\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x00\x00\x00\x21\xf9\x04'
    b'\x01\x0a\x00\x01\x00\x2c\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02'
    b'\x02\x4c\x01\x00\x3b'
)

@override_settings(MEDIA_ROOT='app/test/testdata/')
class PetTest(TestCase):
  def test_add_pet(self):
        client = Client()
        user = User.objects.create_user(username=email, password=password)
        client.login(username=email, password=password)

        # 이미지 추가
        image = SimpleUploadedFile('small.gif', small_gif, content_type='image/gif')
        response = client.post(reverse('petGarden:pet'), {
            'image': image,
            'name': 'puppy'
        })
        self.assertEqual(response.status_code, 200)

settingsMEDIA_ROOT를 오버라이딩 시켜서 testdata 라는 폴더에 넣었다 (나중에 한번에 지우기 위해서 한 폴더에 몰아넣자)

그러고 API가 전송되면 파일이 업로드 되는 것처럼 MEDIA_ROOT에 저장된다
이런 방법으로 넣어야 ImageField가 인식하더라.....

주의❗️
API 전송 한번에 SimpleUploadFile을 하나씩 넣어야한다. 전역으로 넣으니까 한번 업로드 한거라 그런지 form 유효성 검사에 걸리더라...

JSON 테스트

서버를 나누다보니 JSON 형태로 데이터를 보내는 것이 편하여 사용하게되었는데, 테스팅 코드에서도 JSON을 보낼 수 있다

client.login(username=email, password=password)
response = client.post(
    reverse('petGarden:add_photo', args=[pet.id]),
    json.dumps({
      "image_url": 'image_url',
      'thumbnail_url': "thumbnail_url",
      'text': 'Good Photo'
    }),
    content_type='application/json'
)

json.dumps()함수로 바이트 코드로 변경하고 content_type을 JSON으로 보내주면 된다