有没有标准方法来模拟Django模型?

时间:2017-10-19 16:07:52

标签: django unit-testing django-models orm tdd

我有一个名为Pdb的模型:

class Pdb(models.Model):

    id = models.TextField(primary_key=True)
    title = models.TextField()

与模型Residue建立一对多的关系:

class Residue(models.Model):

    id = models.TextField(primary_key=True)
    name = models.TextField()
    pdb = models.ForeignKey(Pdb)

单元测试Pdb很好:

def test_can_create_pdb(self):
    pdb = Pdb(pk="1XXY", title="The PDB Title")
    pdb.save()
    self.assertEqual(Pdb.objects.all().count(), 1)
    retrieved_pdb = Pdb.objects.first()
    self.assertEqual(retrieved_pdb, pdb)

当我单元测试Residue时,我只想使用模拟Pdb对象:

def test_can_create_residue(self):
    pdb = Mock(Pdb)
    residue = Residue(pk="1RRRA1", name="VAL", pdb=mock_pdb)
    residue.save()

但这失败了,因为它需要一些名为_state的属性:

AttributeError: Mock object has no attribute '_state'

所以我不断添加模拟属性,使其看起来像一个真实的模型,但最终我得到了:

django.db.utils.ConnectionDoesNotExist: The connection db doesn't exist

我不知道如何模拟对数据库的实际调用。有没有标准的方法来做到这一点?我真的不想在测试数据库中实际创建Pdb记录,因为那时测试不会被隔离。

是否有既定的最佳实践方法?

我得到的大部分SF和谷歌搜索结果与模拟模型的特定方法有关。任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:4)

由于您涉及数据库,因此您不是严格的单元测试,我会称之为集成测试,但这是另一个非常激烈的争论!

我的建议是让你的包装测试类继承自django.test.TestCase。如果您担心每个单独的测试用例被完全隔离,那么您可以使用每个类的测试方法创建多个类。

如果这些测试完全需要编写,也可能值得重新考虑,因为它们似乎只是在验证框架是否有效。

答案 1 :(得分:0)

哦,我设法通过一个名为“混音器”的库来解决这个问题......

from mixer.backend.django import mixer

def test_can_create_residue(self):
    mock_pdb = mixer.blend(Pdb)
    residue = Residue(pk="1RRRA1", name="VAL", pdb=mock_pdb)
    residue.save()

仍然认为django应该提供一种原生方式来做到这一点。它已经提供了许多测试工具 - 这感觉就像正确的单元测试的主要部分。

答案 2 :(得分:0)

我不确定你是什么意思嘲笑Django模型。编写需要某些模型对象的测试的最简单选项是使用test fixture。它基本上是一个在测试运行之前加载到数据库表中的YAML文件。

your answer中你提到mixer,它看起来像是一个随机生成这些测试装置的库。

这些都是很好的工具,但它们仍然需要数据库访问,而且它们比纯单元测试要慢很多。如果您想完全模拟数据库访问,请尝试Django mock queries。它完全模拟了数据库访问层,因此速度非常快,您不必担心外键。当我想测试一些具有简单数据库访问权限的复杂代码时,我会使用它。如果数据库访问有一些复杂的查询条件,那么我坚持使用真正的数据库。

完全披露:我是Django模拟查询项目的次要贡献者。