Django测试客户端不创建数据库条目

3 投票
3 回答
1290 浏览
提问于 2025-04-18 08:44

我正在为我的视图创建单元测试,使用Django自带的测试客户端来模拟请求。我要调用的视图应该在数据库中创建一个对象。然而,当我在测试方法中查询数据库时,发现这个对象并不存在——要么是没有被创建,要么是在视图返回时被丢弃了。

这是我调用的视图:

def apply_to_cmp(request, campaign_id):
    """ Creates a new Application to 'campaign_id' for request.user """
    campaign = Campaign.objects.get(pk = campaign_id)
    if not Application.objects\
                      .filter(campaign = campaign, user = request.user)\
                      .exists():
        application = Application(**{'campaign' : campaign,
                                     'user'     : request.user})
        application.save()

    return HttpResponseRedirect(request.META.get('HTTP_REFERER'))

这是调用它的测试:

def test_create_campaign_app(self):
    """ Calls the method apply_to_cmp in .views """
    c = Client()
    c.login(username = self.username, password = self.password)
    url = '/campaign/' + self.campaign.id + '/apply/'
    response = c.get(url)

    # Check whether request was successful (should return 302: redirect)
    self.assertEqual(response.status_code, 302)

    # Verify that an Application object was created
    app_count = Application.objects\
                .filter(user = self.user, campaign = self.campaign)\
                .count()
    self.assertEqual(app_count, 1)

这是运行测试后的输出:

Traceback (most recent call last):
  File "/test_views.py", line 40, in test_create_campaign_app
    self.assertEqual(app_count, 1)
AssertionError: 0 != 1

方法 apply_to_cmp 确实被调用了,因为 response.status_code == 302,但应用对象仍然没有被创建。我到底做错了什么呢?

3 个回答

0

Client.login 失败了,因为在 setUp 方法中登录系统没有正确初始化。我通过调用 call_command('loaddata', 'initial_data.json') 来解决这个问题,initial_data.json 文件里包含了登录系统的设置。此外,HttpResponseRedirect(request.META.get('HTTP_REFERER')) 由于一些明显的原因没有奏效。我把那部分改成了

if request.META.get('HTTP_REFERER'):
    return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
return HttpResponse()

因此测试也改成了

self.assertEqual(response.status_code, 200)

这个回答是作为对问题的 编辑 发布的,问题是 Django 测试客户端没有创建数据库条目,由 OP kreld 发布,采用 CC BY-SA 3.0 许可。

1

首先,如果你是从 django.test.TestCase 这个类继承的,记得每个测试都是在事务中进行的(官方文档)。

接下来,你可以在项目中添加数据库日志,这样就能看到是否有访问数据库的记录(官方文档)。

最后,确保你在这一行使用了正确的查询方式: filter(user = self.user, campaign = self.campaign)

3

你的代码看起来没有明显的问题,但很明显,要么是你的测试用例有问题,要么是你测试的代码没有按你想的那样工作。现在是时候重新审视你的假设了。

The method apply_to_cmp is definitely being called, since response.status_code == 302

这是你的第一个假设,可能并不正确。如果你检查一下响应对象中的其他细节,可能会对发生了什么有更清晰的了解。例如,查看一下 response.redirect_chain,确认它确实是按照你预期的方向进行重定向的:

response = c.get(url, follow=True)
self.assertEqual(response.redirect_chain, [<expected output here>])

还有其他细节呢?从你提供的代码中,我看不到 self.username 和 self.password 是在哪里定义的。你确定你的登录测试代码是成功的吗?c.login() 会返回 'True' 或 'False' 来表示登录是否成功。在我的测试用例中,我喜欢确认登录是成功的。

login_success = c.login(username = self.username, password = self.password)
self.assertTrue(login_success)

你也可以更广泛地检查一下。如果你检查 Application.objects.filter(user=self.user, campaign=self.campaign) 没有找到任何东西,那检查一下 Application.objects.all() 呢?你知道某个特定的项目不在你的数据库中,但你知道在测试代码运行时,数据库中存储了什么(如果有的话)吗?你期待那里有其他项目吗?确认一下你所期待的是否是真的。

我觉得你可以解决这个问题,但你需要在分析测试用例时更加深入,而不仅仅是看到你的 app_count 变量不等于 1。仔细检查你的响应对象,添加一些调试语句,质疑每一个假设。

撰写回答