【问题标题】:Unit Testing with Django Models and a lot of relations involved使用 Django 模型进行单元测试并涉及很多关系
【发布时间】:2012-08-06 03:02:50
【问题描述】:

或者,“如何设计您的数据库架构以便于进行单元测试?”

顺便说一句,这里有一个与此非常相似的问题: How to test Models in Django with Foreign Keys

我正在尝试为使用框架 Django 的项目遵循 TDD 方法。我正在创建和测试模型及其功能(保存方法、信号……) 和其他依赖模型的高级功能。

我知道单元测试必须尽可能独立,但我发现自己 使用 FactoryBoy 为每个测试创建大量表和关系,所以我的测试不够强大,因为如果模型中的某些内容发生变化,许多测试可能会被破坏。

如何避免所有这些依赖并使测试更干净?

你们建议在实际测试之前避免所有这些样板吗?

最佳做法是什么?

【问题讨论】:

  • @dm03514 高级函数我的意思是对数据运行一些统计计算的函数,但这不是问题,问题是这个函数使用的表有很多各种关系(一对多、多对多等),因此必须为所有模型创建实例来测试某些功能是很痛苦的
  • @dm03514 关于您删除的答案:感谢您的回答和金字塔的链接 =);我想我可能会将我的问题改为如何设计数据库模式以便于进行单元测试?存在什么样的模式?顺便说一句,fixtures 不是处理测试数据的好方法,因为我正在使用问题中提到的 factory boy,例如:[link]lincolnloop.com/blog/2012/may/3/fixtures-and-factories

标签: django unit-testing django-models


【解决方案1】:

我不确定您是否需要那么深入。你不应该根据你想要的测试方式来设计你的软件,你需要调整你的测试方式来适应你正在使用的工具。

假设您想要获得这种粒度级别,例如在测试某些模型时模拟 FK 和 M2M 模型,对吗?类似的东西

class Invoice(models.Model):
    client = models.ForeignKey(Client)

在您的测试中,您只想测试Invoice 模型,而不处理Client 模型。那正确吗?那么,你为什么不也模拟数据库后端,只测试你的模型应该做什么?

我的意思是你不需要达到那个水平。为你的模型添加一些测试,比如信号、方法,检查模型创建是否正常工作(如果你信任数据库,甚至可以避免这种情况),当你需要使用外部模型时,只需创建你需要的东西测试的setUp() 方法。

此外,如果您愿意,您可以使用 Python 的模拟库来模拟您想要的任何内容:http://www.voidspace.org.uk/python/mock/。如果你真的想做 TDD,你可以在每次测试中用它来模拟你的 FK,但是如果你改变了那个模型,你也需要改变所有的 mockers。

【讨论】:

  • 是的,你可能是对的,我走得太远了;目前我正在探索factory boymodel mommy,我认为这正是我想要的(目前,但总是有改进的余地)
【解决方案2】:

没有用于测试的最佳实践列表,它有很多适合您的方法以及您正在从事的特定项目。我同意 pyriku 的说法:

你不应该根据你想要的测试方式来设计你的软件

但是,我想补充一下,如果你有一个好的模块化软件设计,它应该很容易正确测试。

我最近在工作中对单元测试有点兴趣,我发现了一些 Python 中有趣且有用的工具,FactoryBoy 就是其中之一,而不是准备很多在您的测试类的 setUp() 方法中创建对象,您可以为每个模型定义一个工厂,并在需要时批量生成它们。

你也可以试试 Mocker,它是一个模拟对象的库,因为在 Python 中一切都是一个对象,你也可以模拟函数,如果你需要测试一个在一天中的某个时间生成 X 事件的函数,例如,在上午 10:00 发送一条消息,您编写一个总是返回 '10:00am' 的 datetime.datetime.now() 模拟并调用那个函数with那个模拟。

如果您还需要测试一些前端或者您的测试需要一些人工交互(例如针对 执行 OAuth 时),您可以使用 Selenium 填写和提交这些表单。

在你的情况下,要准备与 FactoryBoy 有关系的对象,你可以尝试覆盖 Factory._prepare() 方法,让我们用这个简单的 django 模型来做:

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(User, blank=True, null=True)

    # ...

现在,让我们定义一个简单的 UserFactory:

class UserFactory(factory.Factory):
    FACTORY_FOR = User

    first_name = 'Foo'
    last_name = factory.Sequence(lambda n: 'Bar%s' % n)
    username = factory.LazzyAttribute(lambda obj: '%s.%s' % (obj.first_name, obj.last_name))

现在,假设我想要或需要我的工厂生成包含 5 个成员的组,GroupFactory 应该如下所示

class GroupFactory(factory.Factory):
    FACTORY_FOR = Group

    name = factory.Sequence(lambda n: 'Test Group %s' % n)

    @classmethod
    def _prepare(cls, create, **kwargs):
        group = super(GroupFactory, cls)._prepare(create, **kwargs)
        for _ in range(5):
            group.members.add(UserFactory())
        return group

希望这对您有所帮助,或者至少给您带来了启发。在这里,我将留下一些与我提到的工具相关的资源链接:

工厂男孩:https://github.com/rbarrois/factory_boy

嘲讽者:http://niemeyer.net/mocker

硒:http://selenium-python.readthedocs.org/en/latest/index.html

还有一个关于测试的有用线程:

What are the best practices for testing "different layers" in Django?

【讨论】:

    【解决方案3】:

    尝试使用Mixer。它比 'factory_boy' 简单得多,而且功能更强大。您无需设置工厂,并在需要时获取数据:

    from mixer.backend.django import mixer
    
    mixer.blend(MyModel)
    

    【讨论】:

      猜你喜欢
      • 2021-03-02
      • 2019-04-22
      • 1970-01-01
      • 1970-01-01
      • 2014-06-27
      • 2012-01-11
      • 2013-05-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多