在 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 的主要目标是摆脱固定装置;它的典型用例是:
- 设计您的
Factory
类,这基本上是获得“真实”对象实例的方法
- 在您的测试中,只调用您需要的工厂,只指定该测试用例的参数。
据我了解,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 视图结果的内省等提供帮助。
我正在尝试为 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 的主要目标是摆脱固定装置;它的典型用例是:
- 设计您的
Factory
类,这基本上是获得“真实”对象实例的方法 - 在您的测试中,只调用您需要的工厂,只指定该测试用例的参数。
据我了解,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 视图结果的内省等提供帮助。