Capybara、Devise、CanCan和RSpec集成测试:有效的登录会302重定向到example.com

3
更新:请参见本文结尾,了解现在我将规范放在spec/requests中的工作方式。仍然不知道如何为我的控制器获取有效的已登录用户以进行集成测试。
我第一次使用Devise和CanCan,并且发现难以执行最基本的集成测试,即验证已登录的用户是否已登录。我已经阅读了无数与Devise和RSpec集成测试(例如通过直接访问会话加速测试https://github.com/plataformatec/devise/wiki/How-To%3a-Test-with-Capybarahttp://schneems.com/post/15948562424/speed-up-capybara-tests-with-deviseCapybara,RSpec和Devise:是否有任何方法通过绕过缓慢的登录并直接设置会话来使集成测试更快?),但我甚至无法像预期的那样使标准帖子正常工作,真是困惑:
1.开发环境正常工作(可以登录并被重定向到正确的页面,导航登录链接更改为注销等)。
2.test.log没有问题,直到它进行302重定向到http://www.example.com而不是我的root_path。
从众多遇到类似问题的用户可以看出,在不同的情况下,会话等各个方面都无法使用,所以我显然不是唯一一个遇到这个问题的人。
简化的测试内容:
subject { page }

describe 'should be able to log in' do
  before do
    visit '/users/sign_in'
    user = FactoryGirl.create(:admin)
    #user.add_role :admin
    fill_in 'Username', with: user.username
    fill_in 'Password', with: user.password
    click_on 'Sign in'
  end
  it { should have_link 'Logout' }
end
...

相关的log.test输出:

 Started POST "/users/sign_in" for 127.0.0.1 at 2012-11-06 17:31:10 -0800
 Processing by Devise::SessionsController#create as HTML
 Parameters: {"utf8"=>"✓", "user"=>{"username"=>"username1", "password"=>"[FILTERED]",        "remember_me"=>"0"}, "commit"=>"Sign in"}
 [1m[36mUser Load (0.1ms)[0m  [1mSELECT "users".* FROM "users" WHERE "users"."username" =   'username1' LIMIT 1[0m
 [1m[35m (0.0ms)[0m  SAVEPOINT active_record_1
    [1m[36m (0.0ms)[0m  [1mRELEASE SAVEPOINT active_record_1[0m
 [1m[35m (0.0ms)[0m  SAVEPOINT active_record_1
 [1m[36m (0.1ms)[0m  [1mUPDATE "users" SET "last_sign_in_at" = '2012-11-07   01:31:10.722712', "current_sign_in_at" = '2012-11-07 01:31:10.722712', "last_sign_in_ip" =   '127.0.0.1', "current_sign_in_ip" = '127.0.0.1', "sign_in_count" = 1, "updated_at" = '2012-11-  07 01:31:10.723051' WHERE "users"."id" = 1[0m
 [1m[35m (0.0ms)[0m  RELEASE SAVEPOINT active_record_1
 Redirected to http://www.example.com/
 Completed 302 Found in 12ms (ActiveRecord: 0.0ms)

简化后的routes.rb文件:

authenticated :user do
  root :to => 'home#index'
end
root :to => "home#index"
devise_for :users
resources :users

精简版权限:

class Ability include CanCan::Ability

 def initialize(user)
  user ||= User.new # guest user (not logged in)
  if user.has_role? :admin
    can :manage, :all
  end
  ...

为了更加准确,以下是当前的Gemfile文件:

source 'https://rubygems.org'

gem 'rails', '3.2.8'
gem "bootstrap-sass", "~> 2.1.0.1"
gem 'faker', '1.1.2'
gem 'will_paginate', '~> 3.0.3'
gem 'bootstrap-will_paginate', '~> 0.0.9'
gem 'jquery-rails'
gem 'client_side_validations'
gem 'thin'
gem "devise", "~> 2.1.2"
gem "cancan", "~> 1.6.8"
gem "rolify", "~> 3.2.0"
gem "simple_form", "~> 2.0.4"

group :assets do
   gem 'sass-rails',   '~> 3.2.3'
   gem 'coffee-rails', '~> 3.2.1'
   gem 'uglifier', '>= 1.0.3'
end

group :development, :test do
 gem 'sqlite3', '1.3.5'
 gem "rspec-rails", "~> 2.11.4"
 gem 'guard-rspec', '1.2.1'
 #gem 'listen', github: 'guard/listen', branch: 'polling/double'
 gem 'guard-spork'
 gem 'guard-cucumber'
 gem 'spork', '0.9.2'
 gem "factory_girl_rails", ">= 4.1.0"
end

group :development do
  gem 'annotate', '2.5.0'
  gem "quiet_assets", ">= 1.0.1"
end

 group :test do
  gem 'capybara', ">= 1.1.2"
 gem "email_spec", ">= 1.2.1"
 gem "cucumber-rails", ">= 1.3.0", :require => false
 gem "database_cleaner", ">= 0.9.1"
 gem "launchy", ">= 2.1.2"
 gem 'rb-fsevent', '0.9.1', :require => false
 gem 'growl', '1.0.3'
 gem 'terminal-notifier-guard'
end

group :production do
    gem 'pg', '0.12.2'
 end

还有另一个用户在执行这个基本任务时遇到了问题,但最终发现这只是一个语法错误(请参见使用rspec/devise进行登录集成测试),所以我确定我只是错过了非常明显的东西。

更新:

你一定是在开玩笑吧。放了几个小时后,结果发现我把这个特性放在了spec/controllers里面,而将其切换到spec/requests之后一切都正常了。

我引用:

如果您使用的是Rails,请将您的Capybara规范放在spec/requests或spec/integration中。

找到地址:https://github.com/jnicklas/capybara#readme

然而,我仍然想知道如何在我的控制器上进行集成测试,这些测试需要一个已经登录的用户……对于以前的其他非Devise应用程序,这通常是像以下这样简单的事情:

describe SomeController do

  let(:user) { FactoryGirl.create(:user) }

  before { valid_sign_in user }

...

我的帮助者只是一个简单的软件程序。
  def valid_sign_in(user)
    visit signin_path
    fill_in "Email", with: user.email
    fill_in "Password", with: user.password
    uncheck(:remember_me)
    click_button "Sign In"

    # Sign in when not using Capybara as well.
    cookies[:remember_token] = user.remember_token
  end
1个回答

5
有两个问题需要解决:
1. rspec/controller与rspec/requests之间的区别
我试图在spec/controller下进行一个集成测试。根据https://github.com/jnicklas/capybara#readme,你需要将Capybara规范放在spec/requests或spec/integration中。根据https://dev59.com/zW025IYBdhLWcg3wzZT3#5803121:

请求规范是ActionDispatch::IntegrationTest的薄包装器,它不像控制器规范(它包装了ActionController::TestCase)。即使有一个可用的session方法,我认为它也不受支持(即它可能存在是因为一个被包含在其他实用程序中的模块也包括该方法)。

最初,这可以通过Devise通过类似于helper方法来解决:
# file: spec/requests_helper.rb
def login(user)
  post_via_redirect user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
end

但是,官方/最新的解决方案我已经实施了(但请参见下面第二点原因,它没有解决我的问题),可以在https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara找到。

2. Devise::HelperTests 适用于控制器/视图测试,而不是集成测试。

如果我想对控制器进行一些测试,我可以使用Devise::TestHelpers(通过spec_helper.rb引用),但即使如此,我也无法获得Capybara集成测试的功能,因为Devise::TestHelpers专门为控制器/视图测试编写,而不是集成测试。请参见rspec & devise test helpers

总之,在控制器测试中我想要做的事情无法完成,因为它实际上是一个集成测试,并且会话等在spec/controllers中无法使用。但是,有一些Devise测试助手可用于帮助进行控制器/视图测试。

更新

我稍后重新访问了这个问题,现在我的Warden辅助程序已经在我的集成测试中运行。万岁!这减少了我448个测试中的8-10秒。诀窍是在https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara上添加一个新条目。具体来说:

Capybara-Webkit

如果您在使用capybara-webkit驱动程序时无法使用Warden的login_as方法,请尝试在login_as选项结构中将run_callbacks设置为false

user = Factory.create(:user) 
login_as(user, :scope => :user,
:run_callbacks => false)

总之,现在我不需要为每次需要身份验证的集成测试实际填写表单并提交,而只需使用 login_as(user, :scope => :user, :run_callbacks => false)(当然,可以重构为帮助方法)。

我之前已经做过了,包括将事务性固定设置为false。再次链接到https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara。 - Ted
根据上面的更新,我重新访问并使用Warden::Test::Helpers使其工作。诀窍不在于事务性固定装置(我已经安装了它,实际上也不适用,因为我主要处理的是Capybara/Rack::Test而不是Capybara/Selenium),而是链接维基条目中相对较新的添加:额外的:run_callbacks => false选项。太好了! - Ted
请注意,我只是在提到 Capybara/Rack::Test,而不是你主要处理的 Capybara/Selenium(其中也有一些 Selenium 的内容,但是 run_callbacks 对于 Rack::Test 是一个建议)。 - Ted
使用 :run_callbacks => false 对我很有效,但是当我尝试通过 capybara 点击按钮以显示表单时,sqlite 中会出现数据库锁定错误。有什么想法吗? - pastullo
不确定,尽管我猜测它可能无关?我假设测试可以在没有身份验证的情况下运行(去掉身份验证需求并运行)。 - Ted
显示剩余3条评论

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