devise和rspec-rails - 如何在请求类型规范中登录用户(使用类型::请求标记的规范)?

时间:2015-04-08 08:28:07

标签: devise rspec-rails ruby-on-rails-4.2

环境

Rails 4.2.0
ruby-2.2.1 [ x86_64 ]
devise         3.4.1
rspec-core  3.2.2
rspec-rails   3.2.1

在我的 /spec/rails_helper.rb 中,我为包含type: :controllertype: :request

标记的规范文件添加了设计帮助程序

规格/ rails_helper.rb

ActiveRecord::Migration.maintain_test_schema!

RSpec.configure do |config|
  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
  config.fixture_path = "#{::Rails.root}/spec/fixtures"

  # If you're not using ActiveRecord, or you'd prefer not to run each of your
  # examples within a transaction, remove the following line or assign false
  # instead of true.

  config.use_transactional_fixtures = false

  config.before(:suite) do
    DatabaseCleaner.strategy = :transaction
    DatabaseCleaner.clean_with(:truncation)
  end

  config.before(:suite) do
    begin
      DatabaseCleaner.start
      FactoryGirl.lint
    ensure
      DatabaseCleaner.clean
    end
  end

  config.around(:each) do |example|
    DatabaseCleaner.cleaning do
      example.run # ==================> L-60
    end
  end

  config.include FactoryGirl::Syntax::Methods

  # RSpec Rails can automatically mix in different behaviours to your tests
  # based on their file location, for example enabling you to call `get` and
  # `post` in specs under `spec/controllers`.
  #
  # You can disable this behaviour by removing the line below, and instead
  # explicitly tag your specs with their type, e.g.:
  #
  #     RSpec.describe UsersController, :type => :controller do
  #       # ...
  #     end
  #
  # The different available types are documented in the features, such as in
  # https://relishapp.com/rspec/rspec-rails/docs
  config.infer_spec_type_from_file_location!

  config.include Devise::TestHelpers, type: :controller
  config.include Devise::TestHelpers, type: :request

end

使用该配置,type: controller规范运行正常。但是,在运行type: request规范时,我遇到以下错误:

 Failure/Error: Unable to find matching line from backtrace
 NoMethodError:
   undefined method `env' for nil:NilClass
 # /home/.rvm/gems/ruby-2.2.1@myapp/gems/devise-3.4.1/lib/devise/test_helpers.rb:24:in `setup_controller_for_warden'
 # ./spec/rails_helper.rb:60:in `block (3 levels) in <top (required)>'
 # /home/.rvm/gems/ruby-2.2.1@simplyhomeapp/gems/database_cleaner-1.4.1/lib/database_cleaner/generic/base.rb:15:in `cleaning'
 # /home/.rvm/gems/ruby-2.2.1@simplyhomeapp/gems/database_cleaner-1.4.1/lib/database_cleaner/base.rb:92:in `cleaning'
 # /home/.rvm/gems/ruby-2.2.1@simplyhomeapp/gems/database_cleaner-1.4.1/lib/database_cleaner/configuration.rb:86:in `block (2 levels) in cleaning'
 # /home/.rvm/gems/ruby-2.2.1@simplyhomeapp/gems/database_cleaner-1.4.1/lib/database_cleaner/configuration.rb:87:in `call'
 # /home/.rvm/gems/ruby-2.2.1@simplyhomeapp/gems/database_cleaner-1.4.1/lib/database_cleaner/configuration.rb:87:in `cleaning'
 # ./spec/rails_helper.rb:59:in `block (2 levels) in <top (required)>'

https://github.com/plataformatec/devise/blob/master/lib/devise/test_helpers.rb#L24正在关注

def setup_controller_for_warden #:nodoc:
  @request.env['action_controller.instance'] = @controller  # ==================> L-24
end

我知道@request实例不适用于:请求类型规范,因此错误。

我们可以使用任何帮助程序登录用户:使用Devise时请求类型规范吗?

我发现了一个类似的问题https://github.com/plataformatec/devise/issues/1114reply建议如下:

  

如果您正在进行集成测试,请务必填写登录表单并提交,以传统方式登录您的用户。

但是我希望通过实际登录来获取需要登录用户的规范。

感谢。

2 个答案:

答案 0 :(得分:25)

在SO帖子的帮助下(请参阅下面的参考资料部分),我已成功实现了所需的解决方案。我在下面发布我的工作代码,以防它可以帮助其他人寻找相同的东西:

<强>规格/ rails_helper.rb

  RSpec.configure do |config|
    ....
    ....
    config.include Devise::TestHelpers, type: :controller
    config.include Warden::Test::Helpers, type: :request
  end

<强>规格/ shared_contexts.rb

  RSpec.shared_context "api request global before and after hooks" do
    before(:each) do
      Warden.test_mode!
    end

    after(:each) do
      Warden.test_reset!
    end
  end

  RSpec.shared_context "api request authentication helper methods" do
    def sign_in(user)
      login_as(user, scope: :user)
    end

    def sign_out
      logout(:user)
    end
  end

<强> /spec/requests/api/logout_spec.rb

  require 'rails_helper'
  require 'shared_contexts'


  RSpec.describe "Api Logout", :type => :request do
    include_context "api request authentication helper methods"
    include_context "api request global before and after hooks"

    let(:email) { 'test_user1@example.com' }
    let(:password) { 'password' }

    # Assumes you have FactoryGirl included in your application's test group.
    let!(:user) { create(:user, email: email, password: password) }

    context "DELETE /logout" do
      it "responds with 204 and signs out the signed-in user" do
        sign_in(user)

        # Till not figured out how to assert Warden has successfully logged in the user like we can do in a Devise controller spec by asserting subject.current_user. If anybody knows a way to do it please share.
        # expect(subject.current_user).to_not be_nil 

        delete "/logout"

        expect(response).to have_http_status(204)
      end
    end
  end

我还没有弄清楚如何断言Warden已成功登录用户,就像我们可以通过声明expect(subject.current_user).to_not be_nil在Devise控制器规范中做的那样。如果有人知道如何做,请分享。

<强>参考

谢谢,

Jiggnesh

答案 1 :(得分:3)

虽然这里的流行答案,也在Devise wiki上复制,但没关系,它最简单:

<强>规格/ rails_helper.rb

RSpec.configure do |config|
  # ...
  config.include Devise::Test::IntegrationHelpers, type: :request
end

只需在您的请求规范中使用sign_in即可。这相当于在系统/功能规范或Rails系统/控制器测试中声明include Devise::Test::IntegrationHelpers