소프트웨어 마에스트로/BackEnd(Django)

[백엔드] pytest testcode 개편

alpakaka 2024. 8. 16. 21:47

오늘은.. 미루고 미뤄왔던 pytest testcode 를 예쁘게 바꿔볼 예정이다.

 

어제 fat model 도 끝냈겠다.. 이젠 pytest 를 좀 더 눈에 딱 띄고 예쁘게 해볼 예정이다.

 

일단 해야 할 것은 다음과 같다.

1. 코드가 300줄 이상이므로.. 좀 더 작게 파일로 나누기

2. 완벽히 하드코딩 되어있으므로 변수로 넣는 방식을 사용할 수 있도록 할 것

3. 2의 경우와 마찬가지 이지만 일단 order 의 경우에 다른 order 를 써도 백엔드상에서 가능해야 하므로..

4. mock 객체와 fixture, util function 등등을 잘 사용해서 예쁘게 잘 딱 바꿔보자

 

일단 fixture 를 먼저 밖으로 빼놓았다.

파일을 생성하기 전에 귀찮은 일을 제외하고 싶었기 때문이다.

import pytest
from django.contrib.auth import get_user_model
from django.urls import reverse
from rest_framework.test import APIClient

from todos.models import Category, Todo

User = get_user_model()
client = APIClient()


@pytest.fixture
def create_user(db):
    user = User.objects.create_user(
        username="testuser",
        email="testuser@example.com",
        password="testpassword",
    )
    return user


@pytest.fixture
def authenticated_client(create_user):
    client.force_authenticate(user=create_user)
    yield client
    client.force_authenticate(user=None)  # logout


@pytest.fixture
def create_category(db, create_user):
    category = Category.objects.create(
        user_id=create_user,
        title="Test Category",
        color="#FFFFFF",
        order="0|hzzzzz:",
    )
    return category

이런식으로 모든 fixture 가 파일마다 있었는데 이걸 conftest.py 파일을 만들고 그 안에 작성했다.

# This file is for Pytest Configuration
# This file is used to define fixtures that can be used in multiple test files

import pytest
from rest_framework.test import APIClient

from todos.models import Category, SubTodo, Todo, User

client = APIClient()


@pytest.fixture
def create_user(db):
    user = User.objects.create_user(
        username="testuser",
        email="testuser@example.com",
        password="testpassword",
    )
    return user


@pytest.fixture
def authenticated_client(create_user):
    client.force_authenticate(user=create_user)
    yield client
    client.force_authenticate(user=None)  # logout


@pytest.fixture
def create_category(db, create_user):
    category = Category.objects.create(
        user_id=create_user,
        title="Test Category",
        color="#FFFFFF",
        order="0|hzzzzz:",
    )
    return category


@pytest.fixture
def create_todo(db, create_user, create_category):
    todo = Todo.objects.create(
        user_id=create_user,
        start_date="2024-08-01",
        end_date="2024-08-30",
        category_id=create_category,
        content="Test Todo",
        order="0|hzzzzz:",
        is_completed=False,
    )
    return todo


@pytest.fixture
def create_subtodo(db, create_user, create_todo):
    subtodo = SubTodo.objects.create(
        content="Test SubTodo",
        date="2024-08-01",
        todo=create_todo,
        order="0|hzzzzz:",
        is_completed=False,
    )
    return subtodo

이런식으로 넣으니 위에 있던 @fixture 부분을 전부 제거하고 model을 import 하는 부분도 테스트의 주가 아닌 모델들을 전부 지울 수 있었다. 

 

faker 공부

간단했다. 의미 없는 값을 넣어줄 수 있는 함수였다.!

https://pypi.org/project/Faker/

일단 적용해봤다.

@pytest.mark.django_db
def test_create_todo_success(
    authenticated_client, create_category, create_user
):
    url = reverse("todos")
    data = {
        "user_id": create_user.id,
        "start_date": "2024-08-01",
        "end_date": "2024-08-02",
        "content": "Test Todo",
        "category_id": create_category.id,
        "order": "0|hzzzzz:",
    }
    response = authenticated_client.post(url, data, format="json")
    assert response.status_code == 201
    assert "id" in response.data

여기에서 start_date, end_date, content 는 어떤 것을 넣어도 상관없다! (물론 start가 end 보다 앞서거나 같기만 하면 된다)

 

일단 투두에 넣어봤다.

 

fixture 로 date 와 content 를 불러 올 수 있도록 작성한 후에 이를 테스트에 넣도록 코드를 수정했다.

conftest.py

@pytest.fixture
def date():
    return fake.date_this_year()


@pytest.fixture
def content():
    return fake.sentence(nb_words=6)



# test.py
@pytest.mark.django_db
def test_create_todo_success(
    authenticated_client, create_category, create_user, date, content
):
    url = reverse("todos")
    data = {
        "user_id": create_user.id,
        "start_date": date,
        "end_date": date + timedelta(days=1),
        "content": content,
        "category_id": create_category.id,
        "order": "0|hzzzzz:",
    }
    response = authenticated_client.post(url, data, format="json")
    assert response.status_code == 201
    assert "id" in response.data

일단 이런식으로 작성했다.

그리고 코파일럿에게 이런식으로 수정해달라고했는데, 잘 된줄 알았는데 pytest 를 돌렸더니 못보던 에러가 발생해서 내일 다시 해봐야겠다...

 

mock 객체에 대해서 공부하기

https://codguide.tistory.com/8

 

[Python] 파이썬 유닛 테스트 pytest-mock 사용법

python으로 유닛테스트를 작성하다 보면 제약사항이 생길 수 있습니다. 예를 들어, 방화벽으로 외부망이 금지된 환경에서 테스트를 해야 하거나, 실제 운영환경이 갖추어지지 않은 곳에서 테스트

codguide.tistory.com

이 블로그를 통해서 이해할 수 있었다.

 

일단 내가 작성한 pytest 중에 다른 곳에서 반응을 받아오는 게 따로 있는지.. 좀 고민을 해봐야할 것 같다.

일단은 faker 를 가장 먼저 도입하고 mock 은 이 이후에 해봐야겠다.

 

확실히 어제.. 너무 늦게 자서 오늘 아무것도 못하는 걸보니.. 그냥 1시 이후에는 하던 작업 멈추고 내일 하도록 해야겠다...