使用Django模型进行单元测试并涉及许多关系

时间:2012-08-08 22:21:53

标签: django unit-testing django-models

或者,“如何设计数据库模式以便于单元测试?”

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

我正在尝试使用TDD方法来处理使用Django框架的项目。我正在创建和测试模型及其功能(保存方法,信号......) 以及依赖于模型的其他高级功能。

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

如何避免所有这些依赖关系并使测试更清洁?

在实际测试之前,你们有什么建议避免所有样板?

最佳做法是什么?

3 个答案:

答案 0 :(得分:13)

没有用于测试的最佳实践列表,它有很多适用于您的以及您正在处理的特定项目。当他说:

时,我同意pyriku
  

您不应该根据您的测试方式设计软件

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

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

你也可以尝试 Mocker ,这是一个模拟对象的库,因为在Python 一切是一个对象,你也可以模拟函数,如果你有用需要测试一个在一天的特定时间生成X事件的函数,例如,在上午10:00发送消息,你编写一个datetime.datetime.now()的模拟,它总是返回'10:00am'并调用该功能即模拟。

如果您还需要测试一些前端或者您的测试需要一些人工交互(例如在执行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

Mocker:http://niemeyer.net/mocker

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

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

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

答案 1 :(得分:2)

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

from mixer.backend.django import mixer

mixer.blend(MyModel)

答案 2 :(得分:0)

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

假设您希望获得该级别的粒度,例如在测试某个模型时模拟FK和M2M模型,对吧?像

这样的东西
class Invoice(models.Model):
    client = models.ForeignKey(Client)

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

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

如果你想要,你可以使用Python的模拟库来模拟任何你想要的东西:http://www.voidspace.org.uk/python/mock/。如果您真的想要进行TDD,可以在每次测试中使用它来模拟你的FK,但是如果你改变那个模型,你也需要改变所有的模型。