在 pytest 中使用太多工厂是否被认为是好的做法?

Is it considered good practice using too many factories in pytest?

我正在尝试为 Django/DjangoREST 项目编写测试。我决定使用 pytest。我在专门为 Django 项目编写测试方面经验不足。所以我现在很迷茫。

这是一个例子:

@pytest.mark.django_db
def test_some_view(
    api_client,
    simple_user,
    model1_factory,
    model2_factory,
    ...  # many other model factories
    modelN_factory
):
    # ...
    # creating here other objects that really depends on each other
    # ...
    model2_obj = ...   # model2 object on its own side depends on model3, model4... and so on

    model1_objs = []
    for i in range(10):
        model1_objs.append(model1_factory(some_field=100, some_model2_rel=model2_obj)
    assert len(model1_objs) == 1, "Created items with duplicate `some_field`"

如您所见,我有太多工厂无法在一次测试中使用。但是看着 在我现在的模型结构中,我想不出更好的方法。可以用这么多吗 工厂进行一次测试?或者我应该找到一些与我的表关系相关的问题吗?

感谢任何帮助。提前致谢

factory_boy 的主要目标是摆脱固定装置;它的典型用例是:

  1. 设计您的 Factory 类,这基本上是获得“真实”对象实例的方法
  2. 在您的测试中,只调用您需要的工厂,只指定该测试用例的参数。

据我了解,pytest fixture 用于“设置测试环境”:启动数据库、模拟外部服务等;在数据库中创建对象不适合他们。

我编写代码的方式如下:

# factories.py
import factory
import factory.fuzzy

from . import models

class DivisionFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.Division
    name = factory.Faker('company')

class EmployeeFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.Employee
    username = factory.Faker('username')
    name = factory.Faker('name')
    employee_id = factory.Sequence(lambda n: 'EM199%06d' % n)
    division = factory.SubFactory(DivisionFactory)
    role = factory.fuzzy.FuzzyChoice(models.Employee.ROLES)
    hired_on = factory.fuzzy.FuzzyDate(
        start_date=datetime.date.today() - datetime.timedelta(days=100),
        end_date=datetime.date.today() - datetime.timedelta(days=10),
    )

我们有一个员工工厂和一个部门工厂 - 每个员工都被分配到一个部门。 提供了每个必填字段;如果我们需要为某些对象配置文件创建特定的工厂,可以通过子类化或使用 traits.

添加

我们现在可以编写测试,只传递测试所需的细节:

# tests.py

@pytest.mark.django_db
def test_get_new_hire(api_client):
    employee = factories.EmployeeFactory(
        hired_on=datetime.date.today(),
        division__name="Finance",
    )
    data = api_client.get(f'/employees/{employee.username}')
    assert data['division'] == "Finance"
    assert data['orientation_status'] == 'pending'

附带说明一下,直接使用 Django 的测试运行器不是更有意义吗?它针对 Django 内部进行了更精细的调整:每个测试都可以原生地包装在 sub-transaction 中以提高性能,测试客户端为 in-depth 视图结果的内省等提供帮助。