是否可以在不修补ActiveRecord的情况下使用Selenium / Capybara Webkit / Poltergeist?

时间:2014-06-23 14:46:49

标签: ruby-on-rails activerecord selenium rspec capybara

根据Capybara Docs,有两种不同的策略来处理Selenium无法访问测试中生成的数据的问题:

  1. Monkey patch ActiveRecord:

    类ActiveRecord :: Base   @@ shared_connection = nil   def self.connection     @@ shared_connection || retrieve_connection   结束 结束 ActiveRecord :: Base.shared_connection = ActiveRecord :: Base.connection

  2. 使用截断而不是交易

  3. 我按照Avdi Grimm here的概述设置了DatabaseCleaner,虽然查看我的日志但我可以清楚地看到DatabaseCleaner正在使用截断,例如:

    (7.4ms)TRUNCATE TABLE"画廊","照片","用户" RESTART IDENTITY CASCADE;

    ...当我运行我的功能时,我作为设置的一部分创建的任何模型都不可用于驱动程序。我可以看到场景中存在模型,但如果我将它们打印到页面上,它们就不存在了。

    我可以让驱动程序查看模型的唯一方法是使用上面的ActiveRecord monkey-patch,但这似乎会引发许多奇怪的次要问题 - 测试挂起等等。

    为什么驱动程序(Selenium或Capybara-Webkit)无法看到在测试中创建的模型,即使我使用带有截断策略的DatabaseCleaner,又如何让Capybara使用JavaScript驱动程序呢?

    注意:使用默认驱动程序

    ,测试正常

    我的DatabaseCleaner配置:

    RSpec.configure do |config|
    
      # Clean DB completely
      config.before(:suite) do
        DatabaseCleaner.clean_with(:truncation)
      end
    
      # Set default strategy (non-js/Rack::Test)
      config.before(:each) do
        DatabaseCleaner.strategy = :transaction
      end
    
      # Set strategy for tests using JS (slower)
      config.before(:each, js: true) do
        DatabaseCleaner.strategy = :truncation
      end
    
      # Wrap DatabaseCleaner around each test (before & after)
      config.before(:each) do
        DatabaseCleaner.start
      end
    
      config.after(:each) do
        DatabaseCleaner.clean
      end
    
    end
    

    以下是我希望使用JavaScript进行测试的功能示例:

    feature "User deletes a photo" do
    
      before {
        as_signed_in_user
        @gallery = create(:gallery_with_photos)
      }
    
      scenario "successfully deletes a photo from a gallery", js: true do
    
        photo_title = @gallery.photos.first.title
        puts "GALLERY #{Gallery.find(1)}" # <Gallery:0x007f9b928fe8e8>
        visit gallery_path(@gallery) # JavaScript driver can't find gallery
    
        within '.photos-list' do
          click_link photo_title
        end
        click_link('Delete Photo')
    
        expect(current_path).to eq( gallery_path(@gallery) )
        expect(page).not_to have_link(photo_title)
    
      end
    
    end
    

    使用JavaScript驱动程序时,此测试失败并显示:

      1) User deletes a photo successfully deletes a photo from a gallery
         Failure/Error: Unable to find matching line from backtrace
         ActiveRecord::RecordNotFound:
           Couldn't find Gallery with 'id'=1
    

    正如Dave Schweisguth在评论中所建议的那样,我已经尝试将js:true移出到功能而不是场景,而不会产生任何影响。

1 个答案:

答案 0 :(得分:3)

确保您使用的是数据库清理程序,capybara,selenium-webdriver的最新版本 。还要确保所有这些宝石都在测试组中并且是require: false

然后在spec_helper.rb文件中,您可以通过在这些宝石之前需要rails来控制加载顺序。确保RAILS_ENVuse_transactional_fixtures

的正确设置

<强> spec_helper.rb

require 'rubygems'

ENV['RAILS_ENV'] ||= 'test'

# Load rails + environment
require File.expand_path('../../config/environment', __FILE__)
require 'rspec/rails'

# Load database cleaner
require 'database_cleaner'

# Load capybara and its dependencies
require 'capybara/rspec'
require 'capybara/rails'
require 'selenium-webdriver'

RSpec.configure do |config|
  # Do not use transactional, let database_cleaner do the work
  config.use_transactional_fixtures = false

  # Database cleaner config
  config.before(:suite) { DatabaseCleaner.clean_with :truncation }
  config.before(:each) { DatabaseCleaner.strategy = :transaction }
  config.before(:each, js: true) { DatabaseCleaner.strategy = :truncation }

  config.before(:each) { DatabaseCleaner.start }
  config.after(:each) { DatabaseCleaner.clean }
end

功能规范

尝试使用describe ... type: :feature代替,并将js: true移到最上面,如下所示:

describe "User deletes a photo", type: :feature, js: :true do
  before { ... }
  scenario "successfully deletes a photo from a gallery" do
    ...
  end
end

您的代码

1-即使是为了快速调试,也不要写puts "GALLERY #{Gallery.find(1)}"。它可能会在某些情况下引发错误并导致混淆(即让您认为问题在确实存在的情况下无法解决)。请改用puts "GALLERY #{Gallery.find(@gallery.id)}"

2-在rspec中使用实例变量不是一个好习惯,所以不要这样做:

before {
  as_signed_in_user
  @gallery = create(:gallery_with_photos)
}

你应该写这个

before { as_signed_in_user }
let!(:gallery) { create(:gallery_with_photos) }

请注意,我使用的是let!,它等同于before。因此,在您的其他规范中,您必须将@gallery替换为gallery