【问题标题】:Phoenix: Controller test vs view test?Phoenix:控制器测试与视图测试?
【发布时间】:2017-04-17 19:09:24
【问题描述】:

我有一个控制器:

defmodule ParrotApi.MeetupController do
  use ParrotApi.Web, :controller
  alias ParrotApi.Meetup

  def index(conn, _params) do
    one_hour_ago = Timex.now
                   |> Timex.shift(hours: -1)
                   |> Timex.to_unix
    meetups = from(m in Meetup, where: m.timestamp >= ^one_hour_ago)
              |> Repo.all
              |> Repo.preload(:topic)
    render conn, meetups: meetups
  end
end

以及此控制器的视图:

defmodule ParrotApi.MeetupView do
  use ParrotApi.Web, :view

  def render("index.json", %{ meetups: meetups }) do
    render_many(meetups, ParrotApi.MeetupView, "meetup.json")
  end

  def render("meetup.json", %{ meetup: meetup }) do
    %{
      id: meetup.id,
      timestamp: meetup.timestamp,
      topic: meetup.topic.title,
    }
  end
end

我正在编写我的第一个控制器测试:

defmodule ParrotApi.MeetupControllerTest do
  use ParrotApi.ConnCase
  alias ParrotApi.Meetup
  alias ParrotApi.Topic
  alias ParrotApi.Repo

  require Logger

  describe "#index" do
    test "returns future meetups" do
      topic_title = "Is Jesus your savior?"
      future_time = Timex.now
                    |> Timex.shift(minutes: 1)
                    |> Timex.to_unix
      {:ok, topic} = Topic.changeset(%Topic{}, %{title: topic_title}) |> Repo.insert
      {:ok, meetup} = Meetup.changeset(%Meetup{}, %{timestamp: future_time, topic_id: topic.id}) |> Repo.insert

      conn = build_conn()
      conn = get conn, meetup_path(conn, :index)

      assert json_response(conn, 200) == [
        %{
          "id" => meetup.id,
          "timestamp" => future_time,
          "topic" => topic_title,
        }
      ]
    end

它通过了。然而……这对我来说就像一个视图测试?它正在测试视图操作的装饰/呈现响应。所以我真的不知道该怎么做我的视图测试。

有没有办法只测试render conn, meetups: meetups 响应,然后将当前在我的控制器测试中的内容提取到视图测试中?或者这是错误的测试方式?

【问题讨论】:

  • 这可能是相当主观的,但是当我进行控制器测试时,我主要只是确保我得到了我期望的响应。例如,200 当我希望页面加载时的状态代码,302 用于在用户未登录时进行重定向等。然后在我的视图测试中,我确保加载并显示某些内容。
  • 你愿意举个例子吗?

标签: elixir phoenix-framework


【解决方案1】:

虽然我认为测试最佳实践很大程度上取决于开发人员的意见和偏见,但我强烈同意您的测试是在测试超出其职责范围的内容,并且应该在视图测试中测试实际的 JSON 结构。

就个人而言,我更喜欢测试 json 响应的内容以及控制器测试中的响应状态,但是您的测试对“jsons 的结构如何”而不是“内容是什么”做出了非常强烈的假设。

您通常会将 json 创建留给 json 创建者(您的视图),并且只关心“控制器是否渲染了它应该渲染的内容?”

如果我被逼用一句话来总结测试中最重要的事情,那就是:“在脑海中形成你想要测试的东西,然后试着让你的测试代码读起来完全一样强>”。所以基本上我希望我的测试读起来是这样的:

我期待所有未来聚会的状态码为 200 的 json 编码响应。

test "returns future meetups" do
  topic_title = "Is Jesus your savior?"
  future_time = Timex.now
                |> Timex.shift(minutes: 1)
                |> Timex.to_unix
  {:ok, topic} = Topic.changeset(%Topic{}, %{title: topic_title}) |> Repo.insert
  {:ok, meetup} = Meetup.changeset(%Meetup{}, %{timestamp: future_time, topic_id: topic.id}) |> Repo.insert

  conn = build_conn()
  conn = get conn, meetup_path(conn, :index)

  future_meetups_json = MeetupView.render("index.json", %{meetups: MeetupQuery.future_meetups()})

  assert conn.status == 200
  assert conn.response_body == future_meetups_json
end

将聚会查询隐藏在一个单独的方法中,您可以单独对其进行测试,以确保它真的只返回它应该返回的聚会,并且可以在视图测试中完成实际的视图测试:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-16
    • 2018-07-27
    相关资源
    最近更新 更多