单元测试检查数据库查询是否正确 - 可以模拟什么?

时间:2016-08-11 11:00:35

标签: unit-testing python-3.x mocking django-queryset django-unittest

假设我有一个Article模型,如下所示:

from django.db import models

class Article(models.Model):
    author = models.CharField(max_length=100)
    title = models.CharField(max_length=200)
    body = models.TextField()

与我的实际使用相比,这是天真的简单(author应该是ForeignKey到另一个模型等等,但这种方式更清晰。

想象一下,我想列出某些作者的所有文章的标题,同时保持每个作者的作品。它可以表示为列表列表:

def get_beatles_articles_titles():
    beatles = [
        "John Lennon",
        "Paul McCartney",
        "George Harrison", 
        "Ringo Starrr",
    ]
    return [article.author for author in beatles 
            for article in Article.objects.filter(author=author)]

哦,嵌套列表理解,所以我们的方法并不那么简单。这里有一个很大的错误,所以我们应该以某种方式测试它!最简单的解决方案似乎是创建一些与每个作者相对应的Article实例(并将它们保存在数据库中),并检查是否所有这些实例都已正确获取。

from django.test import TestCase

from models import Article
from views import get_beatles_articles_titles

class ArticlesTitlesTestCase(TestCase):
    def test_that_every_beatles__article_is_fetched(self):
        Article.objects.create(author="John Lennon", title="John's")
        Article.objects.create(author="Paul McCartney", title="Paul's")
        Article.objects.create(author="George Harrison", title="George's")
        Article.objects.create(author="Ringo Starr", title="Ringo's")

        self.assertEqual(get_beatles_articles_titles(), [
            ["John's"],
            ["Paul's"],
            ["George's"],
            ["Ringo's"]
        ])

运行该测试我们可以看到原始代码中存在拼写错误,因此证明了它的实用性。

然而,访问数据库是不赞成嘲笑的事情(难怪,我经历过时间差异可能很大)。在上面的测试中可以嘲笑什么?我特别担心我的.filter查询的正确性(可能会变得相当复杂),所以我不想猜测数据库会给我QuerySet

理想情况下,我想使用这样的东西(伪代码如下):

johns_article = Article(author="John Lennon")
fake_query = MockQuery(author__contains="John")
assertTrue(fakeQuery.contains(johns_article))

1 个答案:

答案 0 :(得分:1)

  

访问数据库不赞成嘲笑事物

这是为什么?常见问题是:

  • 测试速度
  • 测试可靠性
  • 测试flakiness
  • 测试并行化
  • 夹具创建
  • 一般数据管理

如果您需要测试数据库交互,唯一的方法是针对数据库运行测试。 Django让您了解并创建了一个框架,解决了上面列出的所有问题。 django TestCase为你处理所有这些。它管理测试数据库,提供供应工具,在事务中执行所有测试,并在每次事务后通过回滚快速清理。

我绝对同意,对于快速测试,他们应该在单元级别完成并存根所有协作和文件/套接字访问,但在这种情况下,django应该让你满意。

您已经在使用django数据库,该数据库是为每次测试运行而配置的,为什么不访问它?

所以基本上你的测试是正确的,django为它创建了工具,它根本不是反模式。一种常见的策略是进行多层测试。您可以拥有一个“单元测试”或“小测试”框架和测试运行器,它不会在大多数测试中存在IO,其中所有协作都是模拟/存根的。然后在django get_beatles_articles_titles中实现一些测试来测试数据库交互。

如果你从测试/ django开始,从长远来看,有一些事情可能会有所帮助:

  1. 对您的查询进行了描述,bulk_create可以使用较少的查询进行优化和完成
  2. 使用create方法代替多个invariant.js:38 Uncaught (in promise) Error: update(): expected target of $push to be an array; got [object Object].(…)方法
  3. 使用抽象来实例化您的测试模型,我的首选是工厂男孩。这将有助于使您无需更改创建模型所需的内容,还可以创建合理的动态默认值,因此创建过程更加简洁。