کتابخانه factory-boy در پایتون
factory-boy یک ابزار قدرتمند برای تولید دادههای ساختگی (fixtures/fake data) در تستهای نرمافزاری است. این کتابخانه الهام گرفته از factory_girl در دنیای روبی است و به سادگی قابل ترکیب با ORMهای مختلف مانند Django ORM، SQLAlchemy و یا مدلهای ساده پایتون است. هدف اصلی factory-boy تسهیل ایجاد نمونههای مدل برای تستها و جلوگیری از تکرار کدهای ساخت داده است.
چرا از factory-boy استفاده کنیم؟
- تولید خودکار دادههای معتبر برای تستها
- قابلیت تعریف الگوهای قابلاستفاده مجدد (factories)
- پشتیبانی از Faker برای تولید متن، ایمیل، آدرس و …
- سازگاری با تسترانرهایی مثل pytest و unittest
- حذف وابستگی به fixtures ساکن و آسانتر کردن نگهداری تستها
نصب و راهاندازی
نصب ساده است و از pip قابل انجام است:
pip install factory-boy fakerاین دستور factory-boy و faker را نصب میکند. faker برای تولید دادههای تصادفی و واقعیتر بسیار مفید است.
مفاهیم پایه: Factory، build و create
هر factory یک کلاس است که نحوه ساخت یک نمونه از یک مدل را تعریف میکند. دو متد مهم وجود دارد:
- build(): نمونه را در حافظه میسازد ولی در پایگاهداده ذخیره نمیکند.
- create(): نمونه را میسازد و اگر مدل به پایگاهداده متصل باشد، آن را ذخیره میکند.
نمونه ساده برای مدل پایتون
import factory
from faker import Faker
fake = Faker()
class User:
def __init__(self, username, email):
self.username = username
self.email = email
class UserFactory(factory.Factory):
class Meta:
model = User
username = factory.Sequence(lambda n: f"user{n}")
email = factory.LazyAttribute(lambda o: f"{o.username}@example.com")در این مثال، UserFactory نحوهٔ ساخت شیء User را تعریف میکند. Sequence برای تولید نامهای یکتا استفاده شده و LazyAttribute به یک خاصیت دیگر از شیء دسترسی دارد تا ایمیل سازگار با نام کاربری تولید کند.
ادغام با Django
برای پروژههای Django، از DjangoModelFactory استفاده میشود. این کلاس کار ذخیرهسازی در پایگاهداده را آسان میکند.
import factory
from django.contrib.auth.models import User
class DjangoUserFactory(factory.django.DjangoModelFactory):
class Meta:
model = User
username = factory.Sequence(lambda n: f"user{n}")
email = factory.LazyAttribute(lambda u: f"{u.username}@example.com")
is_active = Trueبا استفاده از DjangoUserFactory.create() میتوان یک کاربر در پایگاهداده ساخت. استفاده از Sequence و LazyAttribute مشابه مثال قبل است، با این تفاوت که مدل Django را هدف گرفتهایم.
ادغام با SQLAlchemy
import factory
from myapp.models import User as SAUser
class SAUserFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta:
model = SAUser
sqlalchemy_session = db_session # باید جلسه SQLAlchemy خود را مشخص کنید
id = factory.Sequence(int)
username = factory.Faker('user_name')
email = factory.LazyAttribute(lambda u: f"{u.username}@example.com")در SQLAlchemyModelFactory باید sqlalchemy_session تنظیم شود تا متد create بتواند شی ساختهشده را ذخیره کند. Faker نیز برای تولید username به کار رفته است.
ویژگیهای پیشرفته
- SubFactory: برای رابطههای خارجی (ForeignKey) از SubFactory استفاده کنید تا به صورت خودکار نمونههای مرتبط ساخته شوند.
- RelatedFactory: زمانی که میخواهید شی فرعی به صورت جدا وابسته به شی والد ساخته شود.
- Traits: برای تعریف حالتهای پیشتعریفشده (مثلاً کاربر فعال/مدیر)
- post_generation: برای اجرای کارهای بعد از ساخت شی، مثل افزودن M2M یا انجام عملیات جانبی
مثال SubFactory و post_generation
class ProfileFactory(factory.django.DjangoModelFactory):
class Meta:
model = Profile
user = factory.SubFactory(DjangoUserFactory)
bio = factory.Faker('text')
class GroupFactory(factory.django.DjangoModelFactory):
class Meta:
model = Group
name = factory.Faker('word')
class UserWithGroupsFactory(DjangoUserFactory):
@factory.post_generation
def groups(self, create, extracted, **kwargs):
if not create:
return
if extracted:
for group in extracted:
self.groups.add(group)
else:
self.groups.add(GroupFactory())در این مثال، ProfileFactory بهطور خودکار یک User با SubFactory میسازد. در UserWithGroupsFactory از post_generation برای اضافهکردن گروهها به رابطهٔ M2M استفاده شده است؛ میتوان گروهها را هم به صورت دستی (extracted) ارسال کرد یا اجازه داد factory یک گروه بسازد.
مقایسه سریع build، create و stub
| متد | عملکرد |
|---|---|
| build | نمونه در حافظه ساخته میشود؛ ذخیرهسازی انجام نمیشود |
| create | نمونه ساخته و در پایگاهداده ذخیره میشود (در صورت پشتیبانی) |
| build_stubbed | شی شبیهسازیشده (stub) ساخته میشود که معمولاً برای تستهای سریع مناسب است و به DB وابسته نیست |
نکات حرفهای و بهینهسازی
- از build_stubbed برای تستهایی که نیازی به ارتباط با DB ندارند استفاده کنید تا سرعت تستها افزایش یابد.
- برای مجموعههای بزرگ داده، از bulk_create در SQLAlchemy/Django و بهینهسازی session/transaction استفاده کنید.
- Factoryها را در ماژولهای جدا نگه دارید و به عنوان fixtures در pytest با scope مناسب ارایه دهید.
- از Faker.seed(…) برای تولید دادههای تکرارپذیر در تستها استفاده کنید.
- برای جلوگیری از تداخل، از Sequence یا استفادهٔ پیششمارهها بهره ببرید.
مثال کامل: ترکیب factory با pytest
import pytest
from myapp.factories import DjangoUserFactory
@pytest.mark.django_db
def test_user_creation():
user = DjangoUserFactory.create(username='alice')
assert user.username == 'alice'
assert user.is_activeاین تست نشان میدهد چگونه میتوان از factory در تستهای pytest استفاده کرد. علامت @pytest.mark.django_db تضمین میکند که تست اجازهٔ دسترسی به پایگاهداده را دارد.
جمعبندی
factory-boy ابزاری مفید و منعطف برای تولید داده در تستهای پایتون است. با پشتیبانی از Django، SQLAlchemy و مدلهای ساده پایتون، استفاده از آن باعث خواناتر شدن تستها، کاهش تکرار و مدیریت بهتر دادههای تست میشود. آشنایی با مفاهیمی چون SubFactory، post_generation، LazyAttribute و build_vs_create کلید استفادهٔ مؤثر از این کتابخانه است.
برای یادگیری بیشتر به مستندات رسمی library مراجعه کنید و نمونهها را در پروژهٔ خودتان امتحان کنید تا بهترین شیوهها را متناسب با نیازهای تستتان بیابید.
آیا این مطلب برای شما مفید بود ؟




