ZetCode

Python FactoryBoy

最后修改于 2025 年 3 月 11 日

Python 中的 FactoryBoy 库是生成测试数据的强大工具。它允许您创建具有真实、随机或预定义数据的模型实例,非常适合测试和开发。本教程将通过实际示例介绍 FactoryBoy 的基本和高级用法。

FactoryBoy 在创建夹具、填充数据库以及为单元和集成测试生成测试用例方面特别有用。

安装

要使用 FactoryBoy,您需要先安装它。您可以使用 pip 进行安装

pip install factory_boy

基本用法

此示例为电子商务系统中的客户生成测试数据。

basic_factory.py
import factory

class Customer:
    def __init__(self, full_name, email, phone):
        self.full_name = full_name
        self.email = email
        self.phone = phone

class CustomerFactory(factory.Factory):
    class Meta:
        model = Customer

    full_name = factory.Faker("name")
    email = factory.Faker("email")
    phone = factory.Faker("phone_number")

customer = CustomerFactory()
print(f"Customer: {customer.full_name}, Email: {customer.email}, Phone: {customer.phone}")

在此实际示例中,CustomerFactory 为电子商务平台创建具有真实数据的 Customer 实例。Faker 提供程序生成随机的姓名、电子邮件和电话号码,模拟真实的客户资料。

这对于测试用户注册或订单处理等功能非常有用,无需手动创建测试数据。该工厂通过提供一致、多样的输入来简化单元测试的设置,从而节省时间并减少测试准备中的错误。

与 Django Models 一起使用

此示例为基于 Django 的博客应用程序创建测试用户。

django_factory.py
import factory
from django.contrib.auth.models import User

class BlogUserFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = User

    username = factory.Faker("user_name")
    email = factory.Faker("email")
    first_name = factory.Faker("first_name")
    last_name = factory.Faker("last_name")
    is_active = True

user = BlogUserFactory()
print(f"Blog User: {user.username}, Email: {user.email}, Active: {user.is_active}")

在这里,BlogUserFactory 为 Django 博客应用程序生成 User 实例。它使用 DjangoModelFactory 与 Django 的 ORM 集成,创建具有随机用户名、电子邮件和姓名,以及固定的 is_active 状态的用户。

这对于测试博客系统中的身份验证、权限或帖子创建非常实用。通过设置 is_active=True,工厂可确保用户已准备好进行登录测试,而 Faker 则提供多样化的数据来模拟真实用户场景。

自定义工厂行为

此示例为在线商店的测试套件生成打折商品。

custom_factory.py
import factory

class Product:
    def __init__(self, name, original_price):
        self.name = name
        self.original_price = original_price

class ProductFactory(factory.Factory):
    class Meta:
        model = Product

    name = factory.Faker("catch_phrase")
    original_price = factory.Faker("pydecimal", left_digits=3, right_digits=2, positive=True)

    @factory.post_generation
    def apply_sale(self, create, extracted, **kwargs):
        if extracted:
            self.original_price *= 0.85  # Apply 15% discount

sale_product = ProductFactory(apply_sale=True)
print(f"Product on Sale: {sale_product.name}, Price: ${sale_product.original_price}")

在此场景中,ProductFactory 创建具有吸引人的名称和价格的 Product 实例。post_generation 钩子 apply_sale 可选地应用 15% 的折扣,模拟在线商店的促销活动。

此自定义对于测试定价逻辑或促销功能非常有价值。通过使用 apply_sale=True,开发人员可以按需生成打折商品,确保对常规价格和促销价格进行测试覆盖,而无需重复工厂定义。

使用序列

此示例为会计系统生成唯一的发票号码。

sequence_factory.py
import factory

class Invoice:
    def __init__(self, invoice_number, client_name):
        self.invoice_number = invoice_number
        self.client_name = client_name

class InvoiceFactory(factory.Factory):
    class Meta:
        model = Invoice

    invoice_number = factory.Sequence(lambda n: f"INV-{n:04d}")
    client_name = factory.Faker("company")

invoice1 = InvoiceFactory()
invoice2 = InvoiceFactory()
print(f"Invoice 1: {invoice1.invoice_number}, Client: {invoice1.client_name}")
print(f"Invoice 2: {invoice2.invoice_number}, Client: {invoice2.client_name}")

InvoiceFactory 使用 factory.Sequence 生成具有唯一发票号码(例如,INV-0001、INV-0002)的 Invoice 对象。客户名称是 Faker 提供的随机公司名称,模拟真实的账单数据。

这非常适合测试需要唯一标识符的发票的金融系统。序列可确保没有重复项,模拟生产环境,而 Faker 则为客户名称增加了多样性,以实现全面的测试场景。

使用子工厂

此示例为学生及其注册课程创建测试数据。

subfactory_example.py
import factory

class Course:
    def __init__(self, title, code):
        self.title = title
        self.code = code

class Student:
    def __init__(self, name, course):
        self.name = name
        self.course = course

class CourseFactory(factory.Factory):
    class Meta:
        model = Course

    title = factory.Faker("job")  # Using job titles as course names
    code = factory.Sequence(lambda n: f"CS{n:03}")

class StudentFactory(factory.Factory):
    class Meta:
        model = Student

    name = factory.Faker("name")
    course = factory.SubFactory(CourseFactory)

student = StudentFactory()
print(f"Student: {student.name}, Course: {student.course.title} ({student.course.code})")

在此示例中,CourseFactory 生成具有类似工作名称和唯一代码(例如,CS001)的 Course 对象。StudentFactory 使用 SubFactory 将每个 Student 链接到一个 Course,模拟在教育系统中的注册。

这对于测试学生管理系统或课程注册功能非常实用。子工厂方法可确保相关数据的一致性,使开发人员无需手动创建相关对象即可测试学生和课程之间的交互。

使用项目生成测试订单

此示例为电子商务测试套件创建带有相关项目的测试订单。

order_items_factory.py
import factory

class Item:
    def __init__(self, name, quantity):
        self.name = name
        self.quantity = quantity

class Order:
    def __init__(self, order_id, items):
        self.order_id = order_id
        self.items = items

class ItemFactory(factory.Factory):
    class Meta:
        model = Item

    name = factory.Faker("word")
    quantity = factory.Faker("pyint", min_value=1, max_value=10)

class OrderFactory(factory.Factory):
    class Meta:
        model = Order

    order_id = factory.Sequence(lambda n: f"ORD-{n:05}")
    items = factory.List([factory.SubFactory(ItemFactory) for _ in range(2)])

order = OrderFactory()
print(f"Order ID: {order.order_id}")
for item in order.items:
    print(f"Item: {item.name}, Quantity: {item.quantity}")

ItemFactory 生成具有随机名称和数量(1-10)的 Item 对象。OrderFactory 创建具有唯一订单 ID 和固定两个项目列表的 Order 实例,使用 factory.ListSubFactory

这对于测试电子商务应用程序中的订单处理或库存管理非常实用。该工厂模拟了具有多个项目的真实订单,使开发人员能够有效地测试订单总额、库存更新或结账流程。

通过将项目数量固定为两个,示例保持了输出的可管理性,但该方法可以扩展到可变长度,以用于更复杂的测试用例,从而提高测试设计的灵活性。

具有懒惰属性以实现动态数据的工厂

此示例为支持系统生成具有动态解决时间的测试票证。

lazy_attribute_factory.py
import factory
from datetime import datetime, timedelta

class SupportTicket:
    def __init__(self, ticket_id, issue, created_at, resolved_at):
        self.ticket_id = ticket_id
        self.issue = issue
        self.created_at = created_at
        self.resolved_at = resolved_at

class SupportTicketFactory(factory.Factory):
    class Meta:
        model = SupportTicket

    ticket_id = factory.Sequence(lambda n: f"TICKET-{n:03}")
    issue = factory.Faker("sentence")
    created_at = factory.Faker("date_time_this_year")
    resolved_at = factory.LazyAttribute(lambda obj: obj.created_at + timedelta(days=factory.Faker("pyint", min_value=1, max_value=7).generate()))

ticket = SupportTicketFactory()
print(f"Ticket: {ticket.ticket_id}, Issue: {ticket.issue}")
print(f"Created: {ticket.created_at}, Resolved: {ticket.resolved_at}")

SupportTicketFactory 创建具有唯一票证 ID、随机问题和动态时间戳的 SupportTicket 实例。created_at 字段使用 Faker 来表示今年的日期,而 resolved_at 则使用 LazyAttribute 计算为创建后的 1-7 天。

这对于测试支持票证工作流程(如响应时间或解决跟踪)非常有用。懒惰属性可确保解决时间与创建时间逻辑一致,模拟真实的票证生命周期,而无需硬编码值。

此处不需要 `try-catch` 块,因为没有抛出异常,但工厂的动态特性使其能够适应支持应用程序中的基于时间的特性或报告的测试。

FactoryBoy 的最佳实践

来源

FactoryBoy 文档

在本文中,我们探讨了使用 Python FactoryBoy 库生成测试数据的各种示例,包括基本用法、Django 集成、自定义、序列和子工厂。

作者

我叫 Jan Bodnar,是一名充满热情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在教授编程方面拥有十多年的经验。

列出所有 Python 教程