跳转到主要内容

基于thoughtbot的factory_bot的Ruby的灵活的测试固定替代品。

项目描述

https://github.com/FactoryBoy/factory_boy/workflows/Test/badge.svg https://github.com/FactoryBoy/factory_boy/workflows/Check/badge.svg Latest Version Supported Python versions Wheel status License

factory_boy是基于thoughtbot的factory_bot的固定替代品。

作为一个固定替代工具,它旨在用易于使用的工厂替换静态、难以维护的固定值。

而不是构建一个包含所有可能角落案例组合的详尽的测试设置,factory_boy允许你使用针对当前测试定制的对象,同时只声明测试特定的字段

class FooTests(unittest.TestCase):

    def test_with_factory_boy(self):
        # We need a 200€, paid order, shipping to australia, for a VIP customer
        order = OrderFactory(
            amount=200,
            status='PAID',
            customer__is_vip=True,
            address__country='AU',
        )
        # Run the tests here

    def test_without_factory_boy(self):
        address = Address(
            street="42 fubar street",
            zipcode="42Z42",
            city="Sydney",
            country="AU",
        )
        customer = Customer(
            first_name="John",
            last_name="Doe",
            phone="+1234",
            email="john.doe@example.org",
            active=True,
            is_vip=True,
            address=address,
        )
        # etc.

factory_boy旨在与各种对象关系映射(ORM)工具(Django、MongoDB、SQLAlchemy)良好配合,并且可以轻松扩展到其他库。

其主要功能包括

  • 简洁的声明性语法

  • 在保留全局上下文的同时链式调用工厂方法

  • 支持多种构建策略(已保存/未保存的实例、模拟对象)

  • 支持每个类多个工厂,包括继承

下载

PyPI:https://pypi.ac.cn/project/factory-boy/

$ pip install factory_boy

源代码:https://github.com/FactoryBoy/factory_boy/

$ git clone git://github.com/FactoryBoy/factory_boy/
$ python setup.py install

用法

定义工厂

工厂声明一组用于实例化Python对象的属性。对象的类必须在类Meta属性中的model字段中定义

import factory
from . import models

class UserFactory(factory.Factory):
    class Meta:
        model = models.User

    first_name = 'John'
    last_name = 'Doe'
    admin = False

# Another, different, factory for the same object
class AdminFactory(factory.Factory):
    class Meta:
        model = models.User

    first_name = 'Admin'
    last_name = 'User'
    admin = True

ORM集成

factory_boy通过特定的factory.Factory子类与对象关系映射(ORM)工具集成

  • Django,使用factory.django.DjangoModelFactory

  • Mogo,使用factory.mogo.MogoFactory

  • MongoEngine,使用factory.mongoengine.MongoEngineFactory

  • SQLAlchemy,使用factory.alchemy.SQLAlchemyModelFactory

更多详细信息请参阅ORM部分。

使用工厂

factory_boy支持多种不同的实例化策略:build、create和stub

# Returns a User instance that's not saved
user = UserFactory.build()

# Returns a saved User instance.
# UserFactory must subclass an ORM base class, such as DjangoModelFactory.
user = UserFactory.create()

# Returns a stub object (just a bunch of attributes)
obj = UserFactory.stub()

您可以使用Factory类作为默认实例化策略的快捷方式

# Same as UserFactory.create()
user = UserFactory()

无论使用哪种策略,都可以通过传递关键字参数来覆盖定义的属性

# Build a User instance and override first_name
>>> user = UserFactory.build(first_name='Joe')
>>> user.first_name
"Joe"

还可以在一次调用中创建多个对象

>>> users = UserFactory.build_batch(10, first_name="Joe")
>>> len(users)
10
>>> [user.first_name for user in users]
["Joe", "Joe", "Joe", "Joe", "Joe", "Joe", "Joe", "Joe", "Joe", "Joe"]

真实、随机的值

演示使用随机但真实的值看起来更好;这些真实值还可以帮助发现错误。为此,factory_boy依赖于出色的faker

class RandomUserFactory(factory.Factory):
    class Meta:
        model = models.User

    first_name = factory.Faker('first_name')
    last_name = factory.Faker('last_name')
>>> RandomUserFactory()
<User: Lucy Murray>

可重现的随机值

在测试中使用完全随机数据很快就会成为重现损坏构建的问题。为此,factory_boy提供了位于factory.random模块中的助手来处理它使用的随机种子

import factory.random

def setup_test_environment():
    factory.random.reseed_random('my_awesome_project')
    # Other setup here

延迟属性

大多数工厂属性可以使用在工厂定义时评估的静态值添加,但某些属性(如值由其他元素计算的字段)将需要每次生成实例时分配值。

这些“延迟”属性可以按如下方式添加

class UserFactory(factory.Factory):
    class Meta:
        model = models.User

    first_name = 'Joe'
    last_name = 'Blow'
    email = factory.LazyAttribute(lambda a: '{}.{}@example.com'.format(a.first_name, a.last_name).lower())
    date_joined = factory.LazyFunction(datetime.now)
>>> UserFactory().email
"joe.blow@example.com"

序列

可以使用序列生成特定格式的唯一值(例如,电子邮件地址)。序列通过使用Sequence或装饰器sequence定义。

class UserFactory(factory.Factory):
    class Meta:
        model = models.User

    email = factory.Sequence(lambda n: 'person{}@example.com'.format(n))

>>> UserFactory().email
'person0@example.com'
>>> UserFactory().email
'person1@example.com'

关联

一些对象具有复杂的字段,这些字段本身应从专门的工厂中定义。这是通过SubFactory助手处理的。

class PostFactory(factory.Factory):
    class Meta:
        model = models.Post

    author = factory.SubFactory(UserFactory)

将使用关联对象的策略

# Builds and saves a User and a Post
>>> post = PostFactory()
>>> post.id is None  # Post has been 'saved'
False
>>> post.author.id is None  # post.author has been saved
False

# Builds but does not save a User, and then builds but does not save a Post
>>> post = PostFactory.build()
>>> post.id is None
True
>>> post.author.id is None
True

支持策略

factory_boy支持活动的Python版本以及PyPy3。

调试factory_boy

由于调用链较长,调试factory_boy可能相当复杂。可以通过factory日志器获取详细日志。

有一个辅助函数factory.debug(),可以帮助简化调试。

with factory.debug():
    obj = TestModel2Factory()


import logging
logger = logging.getLogger('factory')
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.DEBUG)

这将产生类似于以下(人工缩进)的消息

BaseFactory: Preparing tests.test_using.TestModel2Factory(extra={})
  LazyStub: Computing values for tests.test_using.TestModel2Factory(two=<OrderedDeclarationWrapper for <factory.declarations.SubFactory object at 0x1e15610>>)
    SubFactory: Instantiating tests.test_using.TestModelFactory(__containers=(<LazyStub for tests.test_using.TestModel2Factory>,), one=4), create=True
    BaseFactory: Preparing tests.test_using.TestModelFactory(extra={'__containers': (<LazyStub for tests.test_using.TestModel2Factory>,), 'one': 4})
      LazyStub: Computing values for tests.test_using.TestModelFactory(one=4)
      LazyStub: Computed values, got tests.test_using.TestModelFactory(one=4)
    BaseFactory: Generating tests.test_using.TestModelFactory(one=4)
  LazyStub: Computed values, got tests.test_using.TestModel2Factory(two=<tests.test_using.TestModel object at 0x1e15410>)
BaseFactory: Generating tests.test_using.TestModel2Factory(two=<tests.test_using.TestModel object at 0x1e15410>)

贡献

factory_boy采用MIT许可证。

应在GitHub Issues中打开问题;尽可能提供pull request。欢迎在邮件列表上提问和建议。

开发依赖项可以安装在一个虚拟环境

$ pip install --editable '.[dev]'

所有pull request都应该通过测试套件,只需简单地运行

$ make testall

为了测试覆盖率,请使用

$ make coverage

要使用特定框架版本进行测试,可以使用tox目标

# list all tox environments
$ tox --listenvs

# run tests inside a specific environment (django/mongoengine/SQLAlchemy are not installed)
$ tox -e py310

# run tests inside a specific environment (django)
$ tox -e py310-djangomain

# run tests inside a specific environment (alchemy)
$ tox -e py310-alchemy

# run tests inside a specific environment (mongoengine)
$ tox -e py310-mongo

打包

对于有兴趣将FactoryBoy打包到下游分发渠道(例如.deb.rpm.ebuild)的用户,以下提示可能有所帮助

依赖关系

该软件包的运行时依赖关系列在setup.cfg中。构建和测试库有用的依赖关系由devdoc extras覆盖。

此外,所有开发/测试任务都通过make(1)驱动。

构建

要运行构建步骤(目前仅用于文档),运行

python setup.py egg_info
make doc

测试

在测试活动Python环境时,运行以下命令

make test

项目详情


下载文件

下载您平台上的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分发

factory_boy-3.3.1.tar.gz (163.9 kB 查看哈希值)

上传时间

构建分发

factory_boy-3.3.1-py2.py3-none-any.whl (36.9 kB 查看哈希值)

上传时间 Python 2 Python 3

由以下支持