10

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

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

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

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

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

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

最佳实践是什么?

4

3 回答 3

13

没有用于测试的最佳实践列表,其中有很多对您和您正在从事的特定项目有用的方法。我同意 pyriku 的说法:

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

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

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

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

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

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

在 Django 中测试“不同层”的最佳实践是什么?

于 2012-08-14T20:36:35.050 回答
2

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

from mixer.backend.django import mixer

mixer.blend(MyModel)
于 2014-01-07T19:27:39.143 回答
0

I'm not really sure if you need to go that deep. You should not design your software basing on how you want to test it, you need to adapt your way of testing to the tools you're using.

Say that you want to get that level of granularity, like mocking FK and M2M models when you test some model, right? Something like

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

and in your tests, you want to test only Invoice model, without dealing with Client model. Is that right? So, why don't you mock the database backend too, and just test ONLY what your model should do?

My point is that you don't need to get to that level. Add some tests to your models for non-trivial things like signals, methods, check that the model creations are working (can even avoid this if you trust the database), and when you need to work with external models just create what you need on the test's setUp() method.

Also if you want you can mock whatever you want using Python's mock library: http://www.voidspace.org.uk/python/mock/. If you really want to do TDD, you can use it for mock your FK in each test, but if you change that model, you will need to change all the mockers too.

于 2012-08-09T16:06:40.030 回答