Заглушення аутентифікації в специфікації запиту


84

Як ви пишете специфікацію запиту, як ви встановлюєте сеанси та / або методи контролера заглушки? Я намагаюся вимкнути автентифікацію в своїх тестах інтеграції - rspec / request

Ось приклад тесту

require File.dirname(__FILE__) + '/../spec_helper'
require File.dirname(__FILE__) + '/authentication_helpers'


describe "Messages" do
  include AuthenticationHelpers

  describe "GET admin/messages" do
    before(:each) do
      @current_user = Factory :super_admin
      login(@current_user)
    end

    it "displays received messages" do
      sender = Factory :jonas
      direct_message = Message.new(:sender_id => sender.id, :subject => "Message system.", :content => "content", :receiver_ids => [@current_user.id])
      direct_message.save
      get admin_messages_path
      response.body.should include(direct_message.subject) 
    end
  end
end

Помічник:

module AuthenticationHelpers
  def login(user)
    session[:user_id] = user.id # session is nil
    #controller.stub!(:current_user).and_return(user) # controller is nil
  end
end

І ApplicationController, який обробляє автентифікацію:

class ApplicationController < ActionController::Base
  protect_from_forgery

  helper_method :current_user
  helper_method :logged_in?

  protected

  def current_user  
    @current_user ||= User.find(session[:user_id]) if session[:user_id]  
  end

  def logged_in?
    !current_user.nil?
  end
end

Чому неможливо отримати доступ до цих ресурсів?

1) Messages GET admin/messages displays received messages
     Failure/Error: login(@current_user)
     NoMethodError:
       undefined method `session' for nil:NilClass
     # ./spec/requests/authentication_helpers.rb:3:in `login'
     # ./spec/requests/message_spec.rb:15:in `block (3 levels) in <top (required)>'

Відповіді:


101

Специфікація запиту - це тонка обгортка ActionDispatch::IntegrationTest, яка не працює як специфікації контролера (яка обертається ActionController::TestCase). Незважаючи на те, що доступний метод сеансу, я не думаю, що він підтримується (тобто, можливо, він є, оскільки модуль, який включається для інших утиліт, також включає цей метод).

Я рекомендую увійти в систему, публікуючи будь-яку дію, яку ви використовуєте для автентифікації користувачів. Якщо ви зробите пароль "паролем" (наприклад) для всіх фабрик користувачів, тоді ви можете зробити щось подібне:

def login (користувач)
  опублікувати login_path,: login => user.login,: password => 'пароль'
кінець

1
Дякую Девід. Це чудово працює, але, здається, трохи надмірно робити всі ці запити?
Jonas Nielsen,

19
Якби я вважав, що це надмірно, я б не рекомендував :)
Девід Челімський

6
Це також найпростіший спосіб зробити це надійно. ActionDispatch::IntegrationTestпризначений для імітації одного або декількох користувачів, які взаємодіють через браузери, без необхідності використовувати реальні браузери. В одному прикладі потенційно може бути більше одного користувача (тобто сеансу) та більше одного контролера, а об’єкти сеансу / контролера - це ті, що використовуються в останньому запиті. Ви не маєте доступу до них перед запитом.
Девід Челімскі,

17
Я повинен використовувати page.driver.postз Капібарою
Ян Ян

@IanYang page.driver.postможе бути антипаттерном , на думку Йонаса Нікласа з Capybara та тестування API )
Епіген

61

Примітка для користувачів Devise ...

До речі, відповідь @David Chelimsky може знадобитися трохи налаштувати, якщо ви використовуєте Devise . Що я роблю під час тестування інтеграції / запитів (завдяки цій публікації StackOverflow ):

# file: spec/requests_helper.rb
def login(user)
  post_via_redirect user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
end

2
коли я потім використовую 'login user1' у специфікації моделі rspec, я отримую невизначену локальну змінну або метод 'user_session_path' для # <RSpec :: Core:
jpw

1
Це передбачає наявність devise_for :usersу вас config/routes.rbфайлу. Якщо ви вказали щось інше, вам доведеться відповідно налаштувати свій код.
fearless_fool

Це працювало у мене, але мені довелося трохи його змінити. Я змінив 'user[email]' => user.emailна, 'user[username]' => user.usernameоскільки моя програма використовує ім’я користувача як логін замість електронної пошти.
webdevguy

3

FWIW, переносячи свої тестові тестові модулі на RSpec, я хотів мати можливість входу в систему за допомогою декількох сесій (розробити) у специфікаціях мого запиту. Потрібно було трохи копати, але це змусило мене працювати. Використання Rails 3.2.13 та RSpec 2.13.0.

# file: spec/support/devise.rb
module RequestHelpers
  def login(user)
    ActionController::IntegrationTest.new(self).open_session do |sess|
      u = users(user)

      sess.post '/users/sign_in', {
        user: {
          email: u.email,
          password: 'password'
        }
      }

      sess.flash[:alert].should be_nil
      sess.flash[:notice].should == 'Signed in successfully.'
      sess.response.code.should == '302'
    end
  end
end

include RequestHelpers

І ...

# spec/request/user_flows.rb
require 'spec_helper'

describe 'User flows' do
  fixtures :users

  it 'lets a user do stuff to another user' do
    karl = login :karl
    karl.get '/users'
    karl.response.code.should eq '200'

    karl.xhr :put, "/users/#{users(:bob).id}", id: users(:bob).id,
      "#{users(:bob).id}-is-funny" => 'true'

    karl.response.code.should eq '200'
    User.find(users(:bob).id).should be_funny

    bob = login :bob
    expect { bob.get '/users' }.to_not raise_exception

    bob.response.code.should eq '200'
  end
end

Редагувати : виправлена ​​помилка


-1

Ви також можете досить легко заглушити сесію.

controller.session.stub(:[]).with(:user_id).and_return(<whatever user ID>)

Усі спеціальні оператори з рубіном - це дійсно методи. Виклик 1+1- це те саме 1.+(1), що означає +лише метод. Аналогічним чином , session[:user_id]одне і те ж , як виклик методу []на session, як іsession.[](:user_id)


Це здається розумним рішенням.
надсвітлий

2
Це не працює в специфікації запиту, а лише в специфікації контролера.
Machisuji

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.