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

时间:2012-11-07 02:14:30

标签: ruby-on-rails rspec devise capybara cancan

更新:请参阅结尾文章,了解规格现在如何工作,我的规格/请求中有我的规格,而不是规格/控制器。仍然想知道如何让我的控制器进行集成测试的有效登录用户。

我第一次使用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 and Devise: any way to make integration tests faster by circumventing slow login and setting session directly?直接访问会话来加快速度,但我甚至无法获得标准职位按预期工作,我感到困惑:

  1. 开发环境工作正常(可以登录并重定向到正确的页面,导航登录链接更改为注销等)。
  2. test.log指示没有问题,直到302重定向到http://www.example.com而不是我的root_path。
  3. 由于各种解决方案存在类似问题的大量用户,我显然不是唯一一个遇到此问题的人,因为会话的各个方面等在不同情况下都不可用。

    剥离测试:

    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
    

    剥离能力:

    班级能力      包括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
    

    还有另一个用户遇到了这个基本任务的问题,但事实证明这只是一个语法错误(请参阅Login integration test with rspec/devise)所以我确定我只是遗漏了一些非常明显的事情。

    更新

    你必须开玩笑吧。离开它几个小时之后,事实证明我在规格/控制器中出于某种原因有这个规格,当简单地将其切换到规格/请求时,一切都变为绿色。

    我引用:

      

    如果您使用的是Rails,请将您的Capybara规格放入规格/要求或   规格/集成。

    找到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 个答案:

答案 0 :(得分:5)

有两个问题在起作用:

<强> 1。 rspec / controller vs rspec / requests

我试图在spec / controller下进行集成测试。根据{{​​3}},您需要将Capybara规格放入spec / requests或spec / integration中。根据{{​​3}}:

  

请求规范是一个瘦的包装器   ActionDispatch :: IntegrationTest,它不像控制器那样工作   specs(包装ActionController :: TestCase)。即使有一个   会话方法可用,我不认为它是支持的(即它是   可能是因为一个模块被包含在其他工具中   还包括那种方法)。

最初这可以通过帮助方法解决Devise,例如:

# 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/jnicklas/capybara#readme

找到

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

如果我想在控制器上做一些测试,我可以使用Devise :: TestHelpers(通过spec_helper.rb引用),但即便如此,我也没有获得Capybara集成测试的功能Devise :: TestHelpers专门用于控制器/视图测试,而不是集成测试。见https://stackoverflow.com/a/5803121/9344

总之,我不想在控制器测试中做什么,因为它实际上是一个集成测试,并且会话等不能用于规范/控制器。但是,有一些Devise测试助手可用于帮助进行控制器/视图测试。

<强>更新

我稍后再重访,现在让我的集成测试中的Warden助手正常运行。欢呼!在我的448测试中,这已经取消了8-10秒。诀窍是https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara上的新条目。具体做法是:

  

Capybara-Webkit

     

如果您在使用Warden的login_as方法时遇到问题   使用capybara-webkit驱动程序,尝试将run_callbacks设置为false   login_as选项struct

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

总之,我不需要每次需要经过身份验证的用户进行集成测试时都要进行实际的表单填写和发布,现在我只需要login_as(user, :scope => :user, :run_callbacks => false)(当然要重构为帮助方法)。