Как выполнить интеграционное тестирование с помощью RSpec и Devise/CanCan?

Если у меня есть пользователь модели Devise, из которых только пользователям с ролью: admin разрешено просматривать определенный URL-адрес, как я могу написать тест интеграции RSpec, чтобы проверить, что статус возвращает 200 для этого URL?

def login(user)
  post user_session_path, :email => user.email, :password => 'password'
end

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

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

describe "GET /users" do
  it "should be able to get" do
    clear_users_and_add_admin #does what it says...
    login(admin)
    get users_path
    response.status.should be(200)
  end
end

ПРИМЕЧАНИЕ!!!: все это изменилось с тех пор, как был задан вопрос. Самый лучший способ сделать это можно здесь: http://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara

Ответ 1

@pchuegr собственный ответ получил меня через линию. Для полноты, это то, что я сделал, что позволяет мне легко настроить как спецификации запросов, так и спецификации контроллера (используя FactoryGirl для создания экземпляра пользователя):

в/spec/support/sign_in_support.rb:

#module for helping controller specs
module ValidUserHelper
  def signed_in_as_a_valid_user
    @user ||= FactoryGirl.create :user
    sign_in @user # method from devise:TestHelpers
  end
end

# module for helping request specs
module ValidUserRequestHelper

  # for use in request specs
  def sign_in_as_a_valid_user
    @user ||= FactoryGirl.create :user
    post_via_redirect user_session_path, 'user[email]' => @user.email, 'user[password]' => @user.password
  end
end

RSpec.configure do |config|
  config.include ValidUserHelper, :type => :controller
  config.include ValidUserRequestHelper, :type => :request
end

Затем в запросе spec:

describe "GET /things" do
  it "test access to things, works with a signed in user" do
    sign_in_as_a_valid_user
    get things_path
    response.status.should be(200)
  end
end

describe "GET /things" do
  it "test access to things, does not work without a signed in user" do
    get things_path
    response.status.should be(302) # redirect to sign in page
  end
end

и аналогичным образом использовать 'signed_in_as_valid_user' в спецификациях контроллера (который обертывает метод sign_in Devise:: TestHelpers с пользователем из FactoryGirl)

Ответ 2

А, так близко. Это делает трюк - мне не хватает правильной формы параметра и перенаправления.

post_via_redirect user_session_path, 'user[email]' => user.email, 'user[password]' => user.password

Ответ 3

Я использовал несколько иной подход, используя Warden:: Test:: Helpers.

В моем spec/support/macros.rb я добавил:

module RequestMacros
  include Warden::Test::Helpers

  # for use in request specs
  def sign_in_as_a_user
    @user ||= FactoryGirl.create :confirmed_user
    login_as @user
  end
end

И затем включил это в конфигурацию RSpec в spec_helper.rb:

RSpec.configure do |config|
  config.include RequestMacros, :type => :request
end

А затем в самих спецификациях запроса:

describe "index" do
  it "redirects to home page" do
    sign_in_as_a_user 
    visit "/url"
    page.should_not have_content 'content'
  end
end

В отличие от метода post_via_redirect user_session_path, это действительно работает и позволяет мне использовать current_user в before_filters, например.

Ответ 4

Вы можете создать макрос (/spec/support/controller_macros.rb) и написать что-то вроде:

module ControllerMacros
  def login_user
    before(:each) do
      @request.env["devise.mapping"] = :user
      @user = Factory(:user)
      sign_in @user
    end
  end
end

Вы также можете указать любые атрибуты CanCan, которые вы хотите. Затем в вашей спецификации:

describe YourController do
    login_user

    it "should ..." do

    end

Ответ 6

По состоянию на середину 2017 года у нас есть еще одна, на мой взгляд, лучшая opprotunity для интеграции разработки в наши Rspec. Мы можем использовать аутентификацию заглушки со вспомогательным методом sign in, как описано ниже:

module ControllerHelpers
    def sign_in(user = double('user'))
      if user.nil?
        allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, {:scope => :user})
        allow(controller).to receive(:current_user).and_return(nil)
      else
        allow(request.env['warden']).to receive(:authenticate!).and_return(user)
        allow(controller).to receive(:current_user).and_return(user)
      end
    end
  end

Вы также должны добавить ссылку spec_helper.rb или rails_helper.rb к вновь созданному файлу:

require 'support/controller_helpers'
  ...
  RSpec.configure do |config|
    ...
    config.include Devise::TestHelpers, :type => :controller
    config.include ControllerHelpers, :type => :controller
    ...
  end

Затем в методе просто поместите в начало тела метода sign_in для контекста для аутентифицированного пользователя, и вы все настроены. Обновленные данные можно найти в devome docs здесь