使用 ExUnit 检查状态代码失败,但实际 REST 测试成功
Check status code with ExUnit failed, but actual REST test success
我正在编写一个控制器函数,它将在呈现 json 对象或错误对象(如果失败时)之前检查条件(关键字是否有效)。
router.ex
scope "/api", DongNghiaWeb do
pipe_through :api
scope "/tim_kiem" do
get "/tu/:word" , APIWordController, :search_word
[...]
word_controller.ex
def search_word(conn, %{"word" => word}) do
conn
|> check_invalid_keyword(word)
|> render("search.json", words: word |> String.trim |> Words.suggest)
end
defp check_invalid_keyword(conn, keyword) do
unless Words.keyword_valid?(String.trim(keyword)) do
conn
|> put_status(400)
|> json(%{
error: "Invalid keyword"
})
end
conn
end
word_controller_test.ex
test "response error when word is not valid", %{conn: conn} do
response = get(conn,api_word_path(conn, :search_word, "a3d"))
|> json_response(400)
assert response["error"] == "Invalid keyword"
end
当运行mix test
时,结果会是这样:
** (RuntimeError) expected response with status 400, got: 200, with body: {"data":[]}
但是当我尝试使用 REST 客户端(例如 Insomnia)进行测试时,json 将 return 变为 { error : "Invalid keyword" }
就好了。
当 Words.keyword_valid?(String.trim(keyword))
为假时,您的代码将响应写入连接两次。
第一次写入发生在您调用 |> json(...)
时,第二次写入发生在您调用 render
时。当已经调用 json
时,您的代码不会阻止调用 render
。浏览器连接在第一次写入后结束,因此您会看到 Insomnia 的正确输出,但测试设置使用写入连接的最后响应。
解决这个问题需要对您的代码进行一些重构。以下是我的做法:
def search_word(conn, %{"word" => word}) do
if Words.keyword_valid?(String.trim(word)) do
conn
|> render("search.json", words: word |> String.trim() |> Words.suggest())
else
conn
|> put_status(400)
|> json(%{
error: "Invalid keyword"
})
end
end
编辑:这是执行您在下面的评论中请求的一种方法:
def search_word(conn, %{"word" => word}) do
with {:ok, conn} <- check_invalid_keyword(conn, word) do
conn
|> render("search.json", words: word |> String.trim() |> Words.suggest())
end
end
def check_invalid_keyword(conn, keyword) do
if Words.keyword_valid?(String.trim(keyword)) do
{:ok, conn}
else
conn
|> put_status(400)
|> json(%{
error: "Invalid keyword"
})
end
end
当关键字无效时,return 值无法匹配 with
子句,并按原样 return 编辑。如果有效,则执行 do
块。
我正在编写一个控制器函数,它将在呈现 json 对象或错误对象(如果失败时)之前检查条件(关键字是否有效)。
router.ex
scope "/api", DongNghiaWeb do
pipe_through :api
scope "/tim_kiem" do
get "/tu/:word" , APIWordController, :search_word
[...]
word_controller.ex
def search_word(conn, %{"word" => word}) do
conn
|> check_invalid_keyword(word)
|> render("search.json", words: word |> String.trim |> Words.suggest)
end
defp check_invalid_keyword(conn, keyword) do
unless Words.keyword_valid?(String.trim(keyword)) do
conn
|> put_status(400)
|> json(%{
error: "Invalid keyword"
})
end
conn
end
word_controller_test.ex
test "response error when word is not valid", %{conn: conn} do
response = get(conn,api_word_path(conn, :search_word, "a3d"))
|> json_response(400)
assert response["error"] == "Invalid keyword"
end
当运行mix test
时,结果会是这样:
** (RuntimeError) expected response with status 400, got: 200, with body: {"data":[]}
但是当我尝试使用 REST 客户端(例如 Insomnia)进行测试时,json 将 return 变为 { error : "Invalid keyword" }
就好了。
当 Words.keyword_valid?(String.trim(keyword))
为假时,您的代码将响应写入连接两次。
第一次写入发生在您调用 |> json(...)
时,第二次写入发生在您调用 render
时。当已经调用 json
时,您的代码不会阻止调用 render
。浏览器连接在第一次写入后结束,因此您会看到 Insomnia 的正确输出,但测试设置使用写入连接的最后响应。
解决这个问题需要对您的代码进行一些重构。以下是我的做法:
def search_word(conn, %{"word" => word}) do
if Words.keyword_valid?(String.trim(word)) do
conn
|> render("search.json", words: word |> String.trim() |> Words.suggest())
else
conn
|> put_status(400)
|> json(%{
error: "Invalid keyword"
})
end
end
编辑:这是执行您在下面的评论中请求的一种方法:
def search_word(conn, %{"word" => word}) do
with {:ok, conn} <- check_invalid_keyword(conn, word) do
conn
|> render("search.json", words: word |> String.trim() |> Words.suggest())
end
end
def check_invalid_keyword(conn, keyword) do
if Words.keyword_valid?(String.trim(keyword)) do
{:ok, conn}
else
conn
|> put_status(400)
|> json(%{
error: "Invalid keyword"
})
end
end
当关键字无效时,return 值无法匹配 with
子句,并按原样 return 编辑。如果有效,则执行 do
块。