6

У меня есть before_filter на моем классе ApplicationController, и я хочу написать для него тест? Где я должен написать этот тест? Я не хочу входить в каждый тестовый файл контроллера подкласса и повторять тест об этом фильтре.Как проверить контроллер приложения перед методами фильтрации в Rails 3?

Следовательно, что является рекомендуемым способом тестирования ApplicationController before_filters?

Обратите внимание, что я использую Rails 3.2.1 с minitest.

ответ

5

Мое случай немного отличается от вашего, но мне нужно было сделать что-то похожее на проверку подлинности на сайте (с помощью 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 

Если есть конкретные контроллеры, которые необходимо пропустить перед фильтром я буду иметь тест, чтобы убедиться, что они пропускают его в тестах на конкретном контроллере. Это не совсем ваша ситуация, поскольку меня интересует эффект метода, а не только зная, что он был вызван, но я думал, что поделюсь, если вы сочтете это полезным.

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 вещь. Это очень удобно для простых сценариев. Если фильтр применяется к множеству действий/контроллеров, вы можете извлечь тестовый код и повторно использовать его среди тестов.

+0

Если фильтр на определенный контроллер, я согласен с вашим подходом. Мой фильтр объявлен в классе 'ApplicationController' (и унаследован от всех контроллеров, полученных из него). Метод фильтра, который я использую, связан с проверками запроса. Мне нужно написать тест в том месте, где я могу использовать такие методы, как те (или эквивалент?), Которые я использую при тестировании конкретного контроллера. Возможно, ваш ответ на мою проблему в вашей последней фразе («Если фильтр ... среди тестов»), но я не вижу этого. Кроме того, как я могу проверить, что метод, прикрепленный к 'before_filter', действительно привязан к нему? –

+1

Вы можете написать набор тестов, который входит в каждый тест контроллера и запускается для каждого действия. Это должно быть просто (r) сделать с помощью 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) –

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 на конкретном действии. Я должен сделать это во всех тестах моих действий.

Если у кого-то еще есть лучшее решение, дайте мне знать.

+0

Почему бы вам просто не проверить, что произойдет с этим фильтром или без него? Таким образом, вы отделяетесь от фильтра и только заботитесь о том, что происходит, если вызывается фильтр, а не он был вызван или нет. Дополнительным преимуществом этого является то, что вы можете переименовать фильтр, изменить его область действия, перенести его на какой-либо другой класс или просто заменить его наблюдателем, службой или тем, что у вас есть. –

2

Улучшение на @bmaddy answser, вам нужно настроить маршрутизацию для запуска спецификаций.

Вот рельсы 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