factory-boy와 Faker로 실제 같은 테스트 데이터 대량 생성하기

테스트 코드의 품질과 생산성을 극적으로 높이는 factory-boy와 Faker 라이브러리로 실제 데이터와 유사한 '살아있는' 테스트 데이터를 대량으로 효율적으로 생성하는 방법

테스트 데이터 생성의 문제점

  • 유지보수 어려움: 모델 필드 변경 시 모든 테스트 코드 수정 필요

  • 다양성 부족: 단순한 고정 데이터로는 현실적인 엣지 케이스 커버 불가

  • 가독성 저하: 데이터 생성 코드가 테스트 핵심 로직을 방해

기본 설정 및 Factory 정의

pip install factory-boy Faker

UserFactory 예시

import factory
from faker import Faker
from django.contrib.auth import get_user_model

fake = Faker('ko_KR')

class UserFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = get_user_model()
        django_get_or_create = ('username',)

    first_name = factory.Faker('first_name', locale='ko_KR')
    last_name = factory.Faker('last_name', locale='ko_KR')
    username = factory.LazyAttribute(lambda o: f'{o.first_name}{o.last_name}_{fake.lexify("????")}')
    email = factory.Faker('email')
    password = factory.PostGenerationMethodCall('set_password', 'defaultpassword123')

핵심 Factory 기능

SubFactory vs LazyAttribute

  • SubFactory: 관계된 객체를 실제로 DB에 생성하여 연결

  • LazyAttribute: 자신의 다른 속성값을 참조하여 동적으로 값 생성

Sequence로 유니크 필드 처리

name = factory.Sequence(lambda n: f'item-{n}')

post_generation으로 M2M 필드 처리

@factory.post_generation
def tags(self, create, extracted, **kwargs):
    if not create:
        return
    if extracted:
        for tag_name in extracted:
            tag, _ = Tag.objects.get_or_create(name=tag_name)
            self.tags.add(tag)

Trait로 팩토리 변형

class Params:
    is_sold_out = factory.Trait(
        status=Product.Status.SOLD_OUT,
        stock=0
    )

테스트 코드에서 Factory 활용

class ProductAPITestCase(APITestCase):
    def setUp(self):
        self.user = UserFactory()
        self.products = ProductFactory.create_batch(5, owner=self.user)
        self.client.force_authenticate(user=self.user)

    def test_list_products(self):
        url = "/api/products/"
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data), 5)

실무 팁

create vs build 전략

  • create(): DB에 실제 레코드 저장, 객체 생성 후 DB 조회 필요한 테스트용

  • build(): 파이썬 객체만 생성, API request body 생성용으로 속도 빠름

재현 가능한 테스트

from faker import Faker
Faker.seed(0)  # 동일한 데이터셋으로 테스트 실행

Factory 파일 관리

  • 각 앱 디렉토리에 factories.py 파일로 관리

  • 공용 Factory는 corecommon 앱에 모아서 관리

Last updated