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

23

环境

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 的 spec 文件包含了 Devise 辅助方法。

spec/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实例在:request类型规范中不可用,因此会出现错误。

在使用Devise时,是否有任何可用的帮助程序可以用于在:request类型规范中登录用户?

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

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

但是,我想要绕过需要已登录用户的规范的实际登录。

谢谢。

2个回答

30

在参考下面的参考文献部分中的一些SO帖子的帮助下,我已经成功实现了所需的解决方案。 为了帮助寻找相同解决方案的其他人,我在下面发布了我的工作代码:

spec/rails_helper.rb

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

spec/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 成功登录用户,就像我们在 Devise 控制器规范中可以通过断言 expect(subject.current_user).to_not be_nil 来实现一样。如果有人知道如何做,请分享。

参考资料

谢谢,

Jiggnesh


你好,Jiggneshh Gohel 你有没有解决以下问题的方案: expect(subject.current_user).to_not be_nil - Viktor Leonets
@ViktorLeonets 抱歉,我现在没有任何解决方案,因为我之后没有继续追求它。 - Jignesh Gohel
谢谢!我花了几个小时寻找解决办法。我认为还有其他的结构方式可能对我更有效(我相信通过支持辅助程序和宏来处理 Request specs,就像在一些示例中处理 Controller specs 的方式一样也能实现相同的效果)。我仍然有一个问题,因为我会得到“未捕获抛出:Warden”的提示,但我必须为我的功能规范解决这个问题。 - CJBrew
1
我按照 Devise 的“如何使用 Capybara 进行测试”的文档,使用以下代码解决了看守器问题:user = FactoryGirl.create(:user) login_as(user, :scope => :user, :run_callbacks => false) - CJBrew
这里不起作用。它抛出了“无法加载此类文件 - shared_contexts”的错误。我正在使用rspec 3.4.4和rails 4.2.0。有什么帮助吗? - Jeffrey C
@JeffreyC shared_contexts 是你的应用程序中一个文件,预期应该位于 <APP_ROOT>/spec/shared_contexts.rb。这在我的答案说明中已经明确提到了。希望能对你有所帮助。谢谢。 - Jignesh Gohel

16

虽然这里的普遍答案,也在Devise wiki上被复制了,是可以的,但最简单的方法就是:

spec/rails_helper.rb

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

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

参考资料

Devise wiki:如何在请求类型规范中登录和退出用户 - 简单方法


1
感谢您分享这个更新。如果您能够分享您找到共享解决方案的维基链接,那将更有帮助。我在Devise的Readme中找到了它。此外,我检查了该模块Devise::Test::IntegrationHelpers,它只是我分享的解决方案的包装器。但是,在应用程序中具有内置功能肯定有助于减少自定义助手的维护工作。谢谢。 - Jignesh Gohel
3
我正在执行 "sign_in @user" 操作,但出现了以下提示信息: "#Proc:0x00005630eaf015d8@/home/humayun/.rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/warden-1.2.9/lib/warden/test/helpers.rb:19"。 - Humayun Naseer

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