如何在Rails 3中测试应用程序控制器的before filter方法?

6
我在我的ApplicationController类上有一个before_filter,我想为它编写一个测试。我应该把这个测试写在哪里?我不想进入每个子类控制器测试文件并重复测试这个过滤器。
因此,测试ApplicationController的before_filters的推荐方法是什么?
请注意,我正在使用Rails 3.2.1和minitest。
4个回答

7

虽然我的情况与你的稍有不同,但是我需要进行类似的操作来测试整个站点的身份验证(使用Devise)。以下是我的做法:

# application_controller.rb
class ApplicationController < ActionController::Base
  before_filter :authenticate_user!
end

# application_controller_test.rb
require 'test_helper'

class TestableController < ApplicationController
  def show
    render :text => 'rendered content here', :status => 200
  end
end

class ApplicationControllerTest < ActionController::TestCase
  tests TestableController

  context "anonymous user" do
    setup do
      get :show
    end
    should redirect_to '/users/sign_in'
  end
end

如果有特定的控制器需要跳过before filter,我会编写测试来确保它们在特定控制器的测试中被跳过。这并不完全符合你的情况,因为我对方法的效果感兴趣,而不仅仅是知道它是否被调用,但我想分享一下,以防你发现它有用。


2

在 @bmaddy 的回答基础上改进,您确实需要设置规划路由来运行规范测试。

这是一个Rails 5的工作示例:

require 'test_helper'

class BaseController < ApplicationController
  def index
    render nothing: true
  end
end

class BaseControllerTest  < ActionDispatch::IntegrationTest
  test 'redirects if user is not logedin' do
    Rails.application.routes.draw do
      get 'base' => 'base#index'
    end

    get '/base'

    assert_equal 302, status
    assert_redirected_to 'http://somewhere.com'
    Rails.application.routes_reloader.reload!
  end

  test 'returns success if user is loggedin' do
    Rails.application.routes.draw do
      get 'base' => 'base#index'
    end

    mock_auth!

    get '/base'
    assert_equal 200, status
    Rails.application.routes_reloader.reload!
  end
end

1

我现在相信我必须测试所有控制器的before_filter存在,并且该过滤器按预期工作。这是因为,我无法知道控制器是否在不应该使用skip_before_filter时使用了它。

因此,我决定使用mock (@controller.expects(:before_filter_method))来确保调用了过滤器。例如,在index操作中,我在我的测试中编写:

test "get index calls the before filter method" do
  @controller.expects(:before_filter_method)
  # fire
  get :index      
end

这将确保我的控制器在特定的操作中调用before_filter_method。我必须在所有的操作测试中都这样做。

如果其他人有更好的解决方案,请让我知道。


为什么不直接测试有或没有该过滤器会发生什么?这样,您就可以与过滤器解耦,并且只关心在调用过滤器时会发生什么,而不是它是否被调用。这样做的附加好处是,您可以重命名过滤器,更改其范围,将其移动到其他类中,或者完全替换为观察者、服务或其他内容。 - n_x_l

0
通常当我需要这样的东西时,我只测试预期的行为,而不考虑这种特定行为可能是在过滤器中实现而不是在方法本身中实现。因此,对于以下简单场景:
class Controller < ApplicationController
  before_filter :load_resource, :only => [:show, :edit]

  def show
  end

  def edit
  end

  def index
  end

  #########
  protected 
  #########

  def load_resource
    @resource = Model.find(params[:id])
  end
end

我会简单测试一下 #show 和 #edit 是否分配了 @resource。这对于简单的场景来说基本上还不错。如果筛选器应用于许多操作/控制器,则可以提取测试代码并在测试之间重复使用。


如果过滤器是针对特定控制器的,我同意你的方法。我的过滤器声明在ApplicationController类上(并继承到所有从它派生的控制器)。我使用的过滤器方法与请求检查有关。我需要在一个可以使用像测试具体控制器时使用的方法(或等效方法?)的地方编写测试。也许你对我的问题的答案在你最后一句话中(“如果过滤器...在测试中”),但我看不出来。另外,如何测试附加到before_filter的方法是否真正附加到它上面? - p.matsinopoulos
1
你可以编写一个测试套件,将其包含在每个控制器测试中,并为每个操作运行。使用RSpec的'it_behaves_like'(https://www.relishapp.com/rspec/rspec-core/v/2-0/docs/example-groups/shared-example-group)应该更容易实现,但是在简单的Test :: Unit中也可以做到这一点。也许你可以从ActiveModel :: Lint:Tests(http://api.rubyonrails.org/classes/ActiveModel/Lint/Tests.html - https://github.com/rails/rails/blob/master/activemodel/lib/active_model/lint.rb)中获得灵感。 - Nikos D

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接