【问题标题】:Unit Testing Controller to Check That Render Is Called单元测试控制器检查渲染是否被调用
【发布时间】:2016-08-30 08:16:00
【问题描述】:

我要做的就是测试控制器是否真的在调用render

注意:

我不关心这个测试的输出,所以我不想检查response.bodyassert_template等。我只想确保render的方法被发送到控制器。

控制器动作:

  def create
    render json: { error: "some error" }, status: :unprocessable_entity
  end

规格:

剔除授权回调:

before do
  allow(AuthenticateRequest).to receive(:call).and_return(true)
  allow_any_instance_of(CanCan::ControllerResource).to receive(:load_resource).and_return(nil)
end

这实际上是规范的唯一期望,因此没有任何冲突。 它“在json中呈现错误”做 参数 = { json:{错误:“一些错误”}, 状态: :unprocessable_entity }

  expect(controller).to receive(:render).with(args)
  post :create, user: { upload: upload }
end

# rspec spec/controllers
# FAILURE:
expected: ({:json=>{:error=>"some error"}, :status=>:unprocessable_entity})
got: (no args)

这是我做错事的可能线索。这就是我删除参数期望时发生的情况:

it "renders error in json" do
  expect(controller).to receive(:render) # no args expectation
  post :create, user: { upload: upload } # called two times??
end

# rspec spec/controllers
# FAILURE:
expected: 1 time with any arguments
received: 2 times with any arguments

我在这里错过了什么?

更新:

这是另一个尝试。又一个错误:

it "renders error in json" do
  expect(controller).to receive(:render).with(
    foo:    { error: "some error" },
    status: :unprocessable_entity
  ).at_least(:once)

  post :create, price_patch_upload: { upload: "upload" }, format: :json
end

结果

...但是当我更改测试以匹配预期时。注意at_least(:once) 的期望。即使控制器渲染两次,你也会认为这会通过:

it "renders error in json" do
  expect(controller).to receive(:render).with(
    json:   { error: "some error" },
    status: :unprocessable_entity
  ).at_least(:once)

  post :create, price_patch_upload: { upload: "upload" }, format: :json
end

结果

???

【问题讨论】:

  • 您的控制器或ApplicationController 中是否有可能呈现两次的before_action?或者,如果您“隐藏”了before_action,比如可以验证或授权的宝石之类的?
  • 啊,是的,好点。是的,我愿意。应用程序控制器调用authenticate_request,用户控制器有CanCan 的load_and_authorize_resource。不过,我想我已经排除了这些方法。我会更新问题以供参考。
  • 我刚刚重现了与您相同的问题。我目前的猜测是它会渲染两次,因为在引擎盖下 rails 仍然会调用render(即使我认为在已经手动调用render 时它不会再这样做了)。到目前为止,我所有的尝试都没有奏效。目前唯一的快速解决方案是expect(response.body).to eq {error: 'some error'}expect(response).to have_http_status(:unprocessable_entity)expect(response.content_type).to eq 'application/json'

标签: ruby-on-rails unit-testing rspec


【解决方案1】:

以下内容已经过测试,现在应该可以工作了:

expect(controller).to receive(:render).with(args).and_call_original
post :create, user: { upload: upload }

.and_call_original 是必需的,因为模拟 render 会使 Rails 认为 render 仍然没有被调用,因为它被模拟的任何底层,因此调用它两次。

.and_call_original 会让被模拟的方法做它该做的事情,并正常运行。

【讨论】:

  • 您先生,是一位学者,一位绅士。谢谢!让我免于拔出更多的头发。
  • @binarymason 不用担心。很高兴能帮上忙:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-29
  • 2021-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-22
相关资源
最近更新 更多