测试控制器,无需通过路由

时间:2013-03-14 16:13:11

标签: ruby-on-rails testing rspec controllers

我正试图孤立地测试我的控制器的动作链。具体来说,我想确保我所需的行为适用于我所有控制器的操作。例如,测试我的所有操作都需要身份验证:

context "when not authenticated" do

  # single case
  describe "GET index" do
    it "responds with 401" do
      get :index
      response.code.should be(401)
    end
  end

  # all of them...
  described_class.action_methods.each do |action|
    ['get', 'put', 'post', 'delete', 'patch'].each do |verb|
      describe "#{verb.upcase} #{action}" do
        it "responds with 401" do
          send verb, action
          response.code.should == "401"
        end
      end
    end   
  end

end

我希望这可行,但事实并非如此。我得到了一些ActionController::RoutingErrors。这是因为我的一些路线需要参数,在某些情况下我不提供它们(就像我打电话给post :create时)。我明白了。但我不明白的是:为什么应该重要!?

对于这些测试,路由是一个单独的问题。我关心的是我的行动链,而不是我的要求(这就是我routing specsrequest specs所要求的。我不应该关心自己在这个级别的路线限制。

所以我的问题是:有没有办法在不模拟请求的情况下测试动作链?

编辑:一些研究

看起来在TestCase#process中正在行使路线。这有必要吗?

2 个答案:

答案 0 :(得分:6)

解决方法是放松路由引擎的约束。这不会绕过路由,但它确实可以更容易地进行测试。

在您的规范中添加以下内容:

before(:all) do
  Rails.application.routes.draw { match ':controller(/:action)' }
end
after(:all) do
  Rails.application.reload_routes!
end

虽然不是问题的严格答案,但这可能是一个很好的解决方法。

答案 1 :(得分:2)

我认为路由不是控制器规范的单独考虑因素。其中一个原因是,根据传递给url的值将值添加到params散列中,并且控制器中的代码可能依赖于这些值。

无论如何,我假设您在ApplicationController中定义了某种授权方法。单独测试每个控制器似乎有点多余。我就是这样做的:

require "spec_helper"

describe ApplicationController do
  describe "require_current_user" do
    ACTIONS_AND_VERBS = [
      [:index,   :get],
      [:show,    :get],
      [:new,     :get],
      [:create,  :post],
      [:edit,    :get],
      [:update,  :put],
      [:destroy, :delete],
    ]

    controller do      
      ACTIONS_AND_VERBS.each do |action, _|
        define_method(action) do
        end
      end
    end

    ACTIONS_AND_VERBS.each do |action, verb|
      describe "#{verb.to_s.upcase} '#{action}'" do
        it "should be successful" do
          send(verb, action, id: -1)
          response.code.should eq("401")
        end
      end
    end
  end
end

在我的ApplicationController我有类似......

class ApplicationController < ActionController::Base
  protect_from_forgery

  before_filter :require_current_user

  def require_current_user
    head :unauthorized
  end
end

编辑:如果我理解正确,我们真正测试的是您的require_current_user或任何您想要发生的等效授权过程正在按预期工作。在这种情况下,我们只能测试一个操作,并相信before_filter正常工作。

require "spec_helper"

describe ApplicationController do
  describe "require_current_user" do
    controller do
      def index
      end
    end

    it 'should head unauthorized for unauthorized users' do
      get :index
      response.code.should eq("401")
    end
  end
end