使用 ConnTest 的 post 发送无效 JSON

Sending invalid JSON with ConnTest's post

我正在尝试测试我的端点是否在无效时返回 400 JSON。如果我这样做:

response = conn
  |> put_req_header("content-type", "application/json")
  |> post(Routes.convert_path(conn, :convert), "invalid JSON")

我会得到一个 Jason.DecodeError,因为它正在尝试对我的字符串进行编码。我怎样才能让它不编码呢?查看文档似乎无法完成:

post(conn, path_or_action, params_or_body \ nil)

还有其他方法可以完成我想要完成的事情吗?

 test/github_omg_web/controllers/convert_controller_test.exs:36
 ** (Plug.Parsers.ParseError) malformed request, a Jason.DecodeError exception was raised with message "unexpected byte at position 0: 0x61 ('a')"
 code: |> post(Routes.convert_path(conn, :convert), "asdf")
 stacktrace:
   (plug) lib/plug/parsers/json.ex:97: Plug.Parsers.JSON.decode/2
   (plug) lib/plug/parsers.ex:271: Plug.Parsers.reduce/7
   (github_omg) lib/github_omg_web/endpoint.ex:1: GithubOmgWeb.Endpoint.plug_builder_call/2
   (github_omg) lib/github_omg_web/endpoint.ex:1: GithubOmgWeb.Endpoint.call/2
   (phoenix) lib/phoenix/test/conn_test.ex:235: Phoenix.ConnTest.dispatch/5
   test/github_omg_web/controllers/convert_controller_test.exs:40: (test)

我创建了一个最小示例以确保我们在同一页面上:

mix phx.new github_omg
mix ecto.create
mix phx.gen.json Accounts User users name:string
mix ecto.migrate
iex -S mix phx.server

要复制,您可以一条一条地粘贴命令,然后按照说明进行操作,例如(phx.gen.json 指示在 lib/github_omg_web/router.ex 中将 resources "/users", UserController, except: [:new, :edit] 添加到 scope "/api"

这是创建了一个同名的新项目 github_omg,一个名为 Accounts 的上下文和一个名为 localhost:4000/api/users

的 api 端点

让我们用 curl:

执行格式错误的请求
curl --write-out '%{http_code}' --silent -X POST -H "Content-Type: application/json" --data "Invalid JSON" --output /dev/null localhost:4000/api/users

我正在使用 --silent--output /dev/null 来隐藏响应 body。在开发模式下,它将输出整个 Phoenix 错误网页并使输出混乱。 --write-out '%{http_code} 显示 http 状态,-H 添加 headers,--data 是我们发送的无效 JSON。

输出将是:

400

看来 Phoenix 正确输出了 400。

在 Phoenix 控制台中,我们将看到:

[info] POST /api/users
[debug] ** (Plug.Parsers.ParseError) malformed request, a Jason.DecodeError exception was raised with message "unexpected byte at position 0: 0x49 ('I')"

所以看起来 Phoenix 在后台自动将 ParseError 转换为 400。实际上,它并没有那么神奇:)

Phoenix 使用 Plug.Exception 协议计算误差。基本上,您可以抛出任何您喜欢的异常。 defexception 宏允许您在异常中附加自定义字段。 Phoenix 查找名为 plug_status 的字段。 Plug.Parser 定义 ParseError 到 return 400。

回到原来的问题:

你误读了堆栈错误。测试框架不会尝试对有效负载进行编码。这是您的应用程序试图对其进行解码(但失败了)。您可以向后阅读堆栈跟踪:

  • 测试调用端点(这已经是您的应用程序代码)
  • 端点调用插件解析器
  • 此插件抛出异常

这意味着您有两个选择:

  1. 你完全可以跳过那个测试。它不是您的应用程序逻辑的一部分,测试您正在使用的框架可能没有意义。
  2. 如果你真的想确定(因为例如,你正在弄乱带有自定义插件的 api 管道),而不是检查 400,你可以检查你的应用程序是否出现正确的错误:
    assert_raise Plug.Parsers.ParseError, fn ->
      conn
      |> put_req_header("content-type", "application/json")
      |> post(Routes.convert_path(conn, :convert), "invalid JSON")
    end

只有在明确设置 conn 的情况下,您才明确测试 400。