在功能齐全的 django 应用程序中测试 POST 时出现 405 错误

Posted

技术标签:

【中文标题】在功能齐全的 django 应用程序中测试 POST 时出现 405 错误【英文标题】:405 error when testing POST in django application that is fully functional 【发布时间】:2014-11-05 17:53:28 【问题描述】:

我正在 django 中开发一个 Web 应用程序,它使用表单 POST 将用户输入的数据放入数据库。

问题: 在 django.test.TestCase 中使用 self.client.post() 进行测试返回 405(不允许请求)。

排除:

POST 不允许 - 应用程序在运行时成功发布(而不是在运行 tests.py 时)。 CSRF 问题 - django.test.TestCase 在运行测试时默认禁用 CSRF 身份验证。 一般测试失败 - 我有几个使用 self.client.get() 可以正常工作的测试

问题: 当我期望 302(重定向)时,可能导致 405 的原因是什么?

这是以这种方式失败的测试之一:

def test_process_one_match_in_title(self):
    #TODO: the server is refusing self.client.post requests in testing, although they work in the app proper.  Why?

    #create a user and an abstract
    this_abstract = Abstract.objects.get(pk=5)  #'hypoplasia' in both title and abstract text
    this_annotator = create_annotator("Joe")
    this_annotator.save()

    userMatchesJSON = "'hypoplasia': 8"

    resp = self.client.post(reverse('diseaseMatcherApp:abstractDetail', kwargs='pk': this_abstract.id),
                            'inputSoFar': 'hypoplasia', 'abstract_pk': this_abstract.id, 'user.id': this_annotator.id,
                             'userMatches': userMatchesJSON)
    self.assertEqual(resp.status_code, 302)  #FAILS HERE; AssertionError: 405 != 302

【问题讨论】:

你发帖的网址是什么? @ChrisHawkes 这是表单调用:
翻译如下: url(r '^process_matches/$', views.process_matches, name='processMatches'),
我只是好奇,因为您说一个应用程序在发布帖子时可以工作,而另一个则不能。我建议两者都发布到该网站的完整网址。 yoursite.com/post_form 可能是 url 是相对的,而不是完全限定的 url。 它实际上不是 2 个应用程序 - 它只是应用程序本身(通过浏览器体验)和该应用程序的测试套件(在 tests.py 中)。相同的页面,相同的表格。我猜在技术上不同的服务器 - 127.0.0.1:8000 与系统创建的 TestServer。但我不明白这是怎么回事,使用 reverse() 来解析 URL。 是的,我同意。我不太熟悉为什么测试服务器不能工作。我想知道这是否是浏览器问题,它没有让请求被发出,但我只是在猜测那里,希望有人解决了类似的问题并可以参与进来。 【参考方案1】:

这个问题让我很生气,这是在搜索该问题时出现的关于该主题的唯一问题。我不能说我有一个适当的修复,但我已经为我解决了,我想我可能会分享几个小时的调试结果。

我遇到了与您完全相同的问题,即 POST 请求在单元测试中不起作用,而在整个应用程序运行时却完全正常。在我的情况下更疯狂的是,测试在我的笔记本电脑上运行,但在我的台式机上运行,​​没有环境差异(相同的虚拟环境,只有 python3.5.3 VS python3.5.1 的细微差别)

我收到的信息是:

“不允许的方法 (POST): url/where/i/wanted/to/post”与测试方法名称交错(使用 ./manage.py test -v2 运行时) “测试总结”中的断言错误:“AssertionError: False is not true : Response did not redirect as expected: Response code was 405 (expected 302)”

使用以下代码:

r = self.client.post(url, follow=True)
self.assertRedirects(r, expected_url)

(我发布到的 URL 由派生自 CreateView 的类处理)

第一条消息是从this piece of code 触发的,它是通过this line 触发的

handler = getattr(self, request.method.lower(), self.http_method_not_allowed)

... request.method 设置为'POST' ...结果我的CreateView在测试模式下没有任何'post'方法(这很奇怪......显然在运行时我的笔记本电脑视图具有该功能!),因此 Django 转而使用“http_method_not_allowed”。

现在,为什么我的 CreateView 没有“POST”方法?我没有花时间完成整个初始化过程(这似乎相当复杂),所以我没有确切的想法,但在我的情况下,一些 URL 在我的测试中根本没有“初始化”(我可以通过在urls/resolvers.py 中打印此函数中的内容来找出答案。例如,应该处理 POST 请求的 URL 我在运行测试时确实不存在......所以我想这解释了为什么我的 CreateView 没有正确初始化

我终于发现我的问题来自使用 django-crudbuilder 应用程序来构建我的 CRUD 视图,并且当数据库为空时它没有正确初始化(在非持久性数据库上运行测试时就是这种情况)

不确定在其他情况下该问题可能来自哪里,但如果您遇到此问题,您可能希望在运行测试时检查您的所有 URL 是否被 Django 正确“看到”(如果它们不是) t,试着找出原因)。

(我链接的Django源代码来自当前的master,但它与我使用的版本1.11.7中的相同)

【讨论】:

【参考方案2】:

我们遇到了同样的问题,正如 @theenglishway 所描述的那样。不幸的是,我们找不到问题的根源。但我们找到了适合我们的解决方案。

解决方案

基本上,我们停止使用来自 django 的 APIClient rest_framework.test,并将其替换为 APIRequestFactory。

from rest_framework.test import APIRequestFactory
from my_app.views import MyAPIsView


# Client for API Request Factory
request_factory = APIRequestFactory()

# Class based view that you want to test
view = MyAPIsView.as_view()

# Request object
request = request_factory.post('/your/post/endpoint/')

# Response obtained from request returned from view
response = view(request)

【讨论】:

以上是关于在功能齐全的 django 应用程序中测试 POST 时出现 405 错误的主要内容,如果未能解决你的问题,请参考以下文章

用于 POS 信用卡刷卡的 Windows 窗体应用程序中的贝宝沙盒测试帐户

冻结数据库以测试 Django 中的新功能

如何在 Django 中测试具有表单的视图?

测试使用南迁移的 Django 应用程序

Django_测试功能

PHP有str_pos函数吗?