为什么xml_http_request需要一个动作作为参数?

时间:2014-01-17 09:54:49

标签: ruby-on-rails rspec xmlhttprequest

通过浏览器的请求只需要其HTTP方法,Rails会根据定义的路由将其调度到适当的控制器操作。那么,为什么xhr无法做到呢?

http://api.rubyonrails.org/classes/ActionController/TestCase/Behavior.html#method-i-xml_http_request

以下代码摘自Hartl's rails tutorial

关系资源的路由定义

SampleApp::Application.routes.draw do
  resources :relationships, only: [:create, :destroy]
end

# relationships POST   /relationships(.:format)       relationships#create
# relationship DELETE /relationships/:id(.:format)   relationships#destroy

控制器

class RelationshipsController < ApplicationController

  def create
    @user = User.find(params[:relationship][:followed_id])
    current_user.follow!(@user)
    respond_to do |format|
      format.html { redirect_to @user }
      format.js
    end
  end

end

视图模板中的表单发送POST请求导致创建新关系,并且在创建关系后,此表单将消失,而另一个表单将显示“取消关注”按钮。

<%= form_for(current_user.relationships.build(followed_id: @user.id)) do |f| %>
  <div><%= f.hidden_field :followed_id %></div>
  <%= f.submit "Follow" %>
<% end %>

此表单只向/relationships发送POST请求。由于Rails将其路由到relationships#create(使用params [:relationship] [:followed_id]),因此无需在此处指定操作。

规范导致通过浏览器发送POST请求。

it "should increment the followed user count" do
  expect do
    click_button "Follow"
  end.to change(user.followed_users, :count).by(1)
end

使用Ajax

形式

<%= form_for(current_user.relationships.build(followed_id: @user.id),
             remote: true) do |f| %>
  <div><%= f.hidden_field :followed_id %></div>
  <%= f.submit "Follow", class: "btn btn-large btn-primary" %>
<% end %>

规范xhr

it "should increment the Relationship count" do
  expect do
    xhr :post, :create, relationship: { followed_id: other_user.id }
  end.to change(Relationship, :count).by(1)
end

所以,我想知道为什么

xhr :post, :create, relationship: { followed_id: other_user.id }

需要:create?我倾向于认为向/relationship发送带有相关对象的POST请求就足够了,即使使用xhr也是如此。

显然,这种混淆来自于我对xhr如何运作的理解不足,现在的问题必须是“xhr如何运作?为什么xhr甚至想要:action?”

1 个答案:

答案 0 :(得分:0)

在您的情况下,xhr需要:create,因为它似乎是控制器规范。控制器规格是一种单元测试,它们绕过路由器(与集成测试/请求规范不同)。一旦绕过路由器,就没有信息应该在测试中调用控制器的哪个动作,因此你必须同时指定方法和动作。

xhr中定义的ActionDispatch::Integration::RequestHelpers方法也用于集成测试(请求规范):

xhr(request_method, path, parameters = nil, headers_or_env = nil)

只要集成测试(和请求规范)是'全栈'测试(并且没有绑定到一个控制器),您需要提供xhr的方法和路径,并且控制器/操作将是由路由器确定。

希望它能回答你的问题。