According to the Capybara Docs there are two separate strategies for handling problems with Selenium not being able to access data generated in a test:
Monkey patch ActiveRecord:
class ActiveRecord::Base @@shared_connection = nil def self.connection @@shared_connection || retrieve_connection end end ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
Use truncation rather than transactions
I've set up DatabaseCleaner as outlined by Avdi Grimm here, and although looking at my logs I can clearly see DatabaseCleaner is using truncation, for example:
(7.4ms) TRUNCATE TABLE "galleries", "photos", "users" RESTART IDENTITY CASCADE;
… when I run my feature, any models I create as part of the setup, are not available to the driver. I can see the models exist within the scenario, but if I print them out to the page, they don't exist.
The only way I can get the driver to see the models is if I use the ActiveRecord monkey-patch above, but this seems to call lots of strange secondary issues - tests hang etc.
Why is it that the driver (Selenium or Capybara-Webkit) cannot see models created in the tests even when I use DatabaseCleaner with a truncation strategy and how can I get Capybara working with a JavaScript driver?
Note: The test passes fine using the default driver
My DatabaseCleaner configuration:
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
Here is an example of a feature that I would like to use JavaScript to test:
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
When using the JavaScript driver, this test fails with:
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
As suggested by Dave Schweisguth in the comments, I have tried moving the js:true
out to the feature rather than the scenario without it having any effect.
Ensure you are using last version of database-cleaner, capybara, selenium-webdriver
. Also make sure that all those gems are in test group and are require: false
Then in your spec_helper.rb file you can control the order of loading by requiring rails before those gems. Ensure also a correct settings for RAILS_ENV
and use_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
Feature spec
Try using describe ... type: :feature
instead, and moving js: true
on top, like this:
describe "User deletes a photo", type: :feature, js: :true do
before { ... }
scenario "successfully deletes a photo from a gallery" do
...
end
end
Your code
1- Do not write puts "GALLERY #{Gallery.find(1)}"
even for a quick debugging. It may raise an error in some case and lead to confusion (i.e make you think that the problem is not solved when indeed it is). Use this instead puts "GALLERY #{Gallery.find(@gallery.id)}"
2- Also it's not a good practice in rspec to use instance variable, so instead of this:
before {
as_signed_in_user
@gallery = create(:gallery_with_photos)
}
Your should write this
before { as_signed_in_user }
let!(:gallery) { create(:gallery_with_photos) }
Note that I use let!
with a bang, it's equivalent to a before
. So in the rest of your spec you must replace @gallery
by gallery
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With