Capybara actions not affecting test database - ruby-on-rails-3

I have an app with users who can post and also vote for other posts. In testing with Capybara, I am having trouble getting the right actions to come through.
I have simplified my tests as much as possible to try to find the error easily, to no avail:
requests/users_spec.rb
require 'spec_helper'
describe "Users" do
before :all do
Warden.test_reset!
Warden.test_mode!
end
describe "when logged in, CAN" do
before :all do
User.all.map(&:destroy)
Post.all.map(&:destroy)
#user = User.create!(email: "foo#bar.baz",
username: "foobar",
password: "foobar",
password_confirmation: "foobar",
confirmed_at: Time.now - 1.minute)
#user2 = User.create!(email: "foo2#bar.baz",
username: "foobar2",
password: "foobar",
password_confirmation: "foobar",
confirmed_at: Time.now - 1.minute)
#post = Post.create!(
title: Faker::Lorem.sentence(5),
content: Faker::Lorem.sentence(25),
post_type: "temporary",
user_id: #user2.id
)
vote = LoggedUpvote.create!(user_id: #user2.id, post_id: #post.id)
login_as(#user, scope: :user)
end
it "create Upvotes", js: true do
count = Upvotes.all.count
visit post_path(#post.obfuscated_id)
page.find('.upvote').click
Upvotes.all.count.should eq(count + 1)
end
end
end
In my spec_helper.rb file I have:
Capybara.run_server = true
Capybara.server_port = 7000
Capybara.app_host = "http://localhost:#{Capybara.server_port}"
Capybara logs in just fine, navigates fine, and performs the actions fine, but the database record for the upvote is not created (despite showing visual confirmation that it has been created and that the right user is logged in).
The upvote is submitted through ajax, and a success response is shown on the screen, but no record is created in the database.
How can I get the record to create?
EDIT My spec_helper file looks like:
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'capybara/rspec'
require 'capybara/rails'
require 'rubygems'
require 'spork'
include Warden::Test::Helpers
# From http://stackoverflow.com/questions/8662554/how-to-use-the-test-database-with-capybara
Capybara.run_server = true
Capybara.server_port = 7000
Capybara.app_host = "http://localhost:#{Capybara.server_port}"
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
Spork.prefork do
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.infer_base_class_for_anonymous_controllers = false
config.order = "random" # From http://railscasts.com/episodes/413-fast-tests
config.include FactoryGirl::Syntax::Methods
config.treat_symbols_as_metadata_keys_with_true_values = true
config.filter_run focus: true
config.run_all_when_everything_filtered = true
config.filter_run_excluding :slow unless ENV["SLOW_SPECS"]
config.before(:all) { DeferredGarbageCollection.start }
config.after(:all) { DeferredGarbageCollection.reconsider }
config.before(:suite) { DatabaseCleaner.strategy = :truncation }
config.before(:each) { DatabaseCleaner.start }
config.after(:each) { DatabaseCleaner.clean }
end
end
Spork.each_run do
FactoryGirl.reload
end

I'm assuming that you are using a driver other than RackTest (based on your mention of ajax, you'd need some sort of javascript support). If so, you may not have setup Capybara correctly to share a database with the selenium driver. See this SO answer, this blog post or this part of the README for more details.

As it turns out, the database setup was fine, but Capybara was not waiting properly for the AJAX transaction to finish.
The solution was to add the following after the Upvote click and before the test assertion:
require "timeout"
Timeout.timeout(Capybara.default_wait_time) do
sleep(0.1) until Upvote.all.count == count + 1
end
And now everything works great.
Source: https://gist.github.com/jnicklas/d8da686061f0a59ffdf7

Related

Feature specs fail only on CircleCI or Codeship continuous integration services

My very basic feature specs are passing just fine locally but failing on CircleCI and Codeship. The tests that are failing:
require 'spec_helper'
describe 'Authentication' do
describe "sign in" do
it "is the landing page for non-authenticated users" do
user = create(:user)
visit root_path
expect( page ).to have_content 'LOG IN' # On sign-in page
fill_in 'Email', with: user.email
fill_in "Password", with: user.password
click_button 'Sign in'
expect( current_path ).to eq user_path(user)
end
end
describe 'registration' do
it "allows new users to register" do
visit root_path
click_link 'Sign up'
fill_in 'Email', with: 'myfake#email.com'
fill_in 'Password', with: 'password'
fill_in 'Password confirmation', with: 'password'
fill_in 'First name', with: 'John'
fill_in 'Last name', with: 'Doe'
click_button "Sign up"
expect( current_path ).to include '/users/'
expect( page ).to have_content "Welcome! You have signed up successfully."
end
end
end
The tests both fail on the first lines where they set expectations of the pages (expect( page ).to have_content "LOG IN" and click_link "Sign up", respectively), with errors suggesting the page HTML is completely blank:
expected to find text "LOG IN" in ""
I saved screenshots on CircleCI, and they indeed show a completely blank page.
Here's where it gets interesting. I tried debugging the problem by running/watching the specs on Circle using a VNC. When I a) set driver: :selenium for the tests, b) add a sleep 1 or two to the tests before testing the page expectations, and c) manually run the test after SSHing into their servers with the VNC, I can see the tests run in Selenium (they open a browser in the VNC) and they pass perfectly.
Outside of the VNC, however, the tests fail consistently in both CI servers. With or without tons of sleeps and driver: :selenium. Any ideas what could be causing this discrepancy between the regular CircleCI/Codeship servers and their VCN/my local test environment? I got in touch with the folks at CircleCI, but they're stumped for the moment.
If relevant, I'm running Ruby 2.2.0, Rails 4.2, Capybara 2.4.4, Capybara-Webkit 1.4.1, and Selenium-Webdriver 2.44.0
Some potentially relevant files:
spec_helper.rb
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "rspec/rails"
require "shoulda/matchers"
require "webmock/rspec"
require 'vcr'
Dir[Rails.root.join("spec/support/**/*.rb")].each { |file| require file }
module Features
include Warden::Test::Helpers
Warden.test_mode!
def sign_in(user)
login_as(user, scope: :user)
end
end
module Controllers
# Pre-parse controller responses for easy access
def response_body
body = JSON.parse(response.body)
body.is_a?(Hash) ? body.to_sh : body.map(&:to_sh)
end
end
module Mock
def disable_webmock(&block)
WebMock.disable!
yield
WebMock.enable!
end
end
RSpec.configure do |config|
config.expect_with :rspec do |c|
c.syntax = :expect
end
# Save a screenshot to CircleCI when a feature test fails
config.after(:each, :type => :feature) do |example|
if example.exception
artifact = save_page
puts "\"#{example.description}\" failed. Page saved to #{artifact}"
end
end
config.include Features, type: :feature
config.include Controllers, type: :controller
config.include Mock
config.include Formulaic::Dsl, type: :feature
config.infer_spec_type_from_file_location!
config.infer_base_class_for_anonymous_controllers = false
config.order = "random"
config.use_transactional_fixtures = false
end
RSpec::Matchers.define :hash_eq do |expected|
match do |actual|
actual.recursive_symbolize_keys == expected.recursive_symbolize_keys
end
end
VCR.configure do |c|
c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
c.hook_into :webmock
c.allow_http_connections_when_no_cassette = true
c.configure_rspec_metadata!
c.ignore_hosts '127.0.0.1', 'localhost:3000'
end
ActiveRecord::Migration.maintain_test_schema!
Capybara.javascript_driver = :webkit
if ENV['CIRCLE_ARTIFACTS']
Capybara.save_and_open_page_path = ENV['CIRCLE_ARTIFACTS']
end
WebMock.disable_net_connect!(allow_localhost: true)
database_cleaner.rb
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
I can think of a couple of things to try:
If you want to get your specs to run headless, try using poltergeist rather than capybara-webkit. poltergeist has several advantages which I described here: https://stackoverflow.com/a/24108439/634576
Selenium might just be a distraction, but, if you want to get it to work, be sure that your CI environment has the right version of Firefox. Each version of selenium-webdriver seems to need a narrow range of versions of Firefox. On CircleCI, you can configure circle.yml to install a specific version of Firefox. Right now we use selenium-webdriver 2.46.2 and Firefox 39.0.3, installed with the following in circle.yml:
dependencies:
pre:
- sudo apt-get update; sudo apt-get install firefox=39.0.3+build2-0ubuntu0.12.04.1
I don't know about Codeship.
I have experienced instability of my Selenium tests on CircleCI until I came across this post https://discuss.circleci.com/t/selenium-tests-timing-out/7765/2
After adding:
machine:
environment:
DBUS_SESSION_BUS_ADDRESS: /dev/null
to my circle.yml, my tests have become stable.

Can't create user for testing using capybara with rspec

I want to test a simple sign in flow, which needs a existed user in the test database.
describe 'Signin page' do
before :each do
User.generate(:email => 'automatic_tester#gmail.com', :password => 'palmdrive', :first_name => 'Automatic', :last_name => 'Tester')
end
it 'signs in a user', :js => true do
signin
current_path.should == redirect_path
end
end
def signin
## Action to sign in the user
end
## Use DatabaseCleaner since selenium driver is used
DatabaseCleaner.strategy = :truncation
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.before :each do
DatabaseCleaner.start
end
config.after :each do
DatabaseCleaner.clean
end
end
Because the way to create a user is a little bit more complicated, so I defined the User.generate method to create a user. To ensure it actually works, in the rails console with test environment, running User.generate(:email => 'automatic_tester#gmail.com', :password => 'palmdrive', :first_name => 'Automatic', :last_name => 'Tester')it successfully created a user in database. Comment out the before :each, the test suite passed successfully.
But the problem is running the codes above, the test fails. It was due to the user can't be created in the database. Why can't the User.generate method create a user?
In the spec_helper add:
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || retrieve_connection
end
end
# Forces all threads to share the same connection. This works on
# Capybara because it starts the web server in a thread.
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
solves the issue. Then no need to use the DatabaseCleaner either
Use create instead
User.create(:....)
Note that create will create a record in your database. Writing to the DB is an expensive operation and you may want to avoid that in some cases by using new rather than create.
A better approach is to use factories. See FactoryGirl https://github.com/thoughtbot/factory_girl

Rspec headaches: configuration advice appreciated

I can't for the life of me seem to set up rspec/capybara in a way that doesn't throw random errors from time to time. My most common errors (but not consistent) are along the lines of:
An error occurred in an after hook
ActiveRecord::RecordNotFound: Couldn't find Post with id=1
occurred at [...]/gems/activerecord-3.2.11/lib/active_record/relation/finder_methods.rb:341:in `find_one'
And a close second is anything related to expecting model objects that were created within the spec and then not found later. e.g. something like
Sorter should populate featured_posts with appropriate posts
Failure/Error: Sorter.top_4.map(&:id).should eq(([best_new_post, best_old_post] + new_posts.last(2).reverse).map(&:id))
expected: [2, 1, 40, 38]
got: []
These tests will always pass when I run them individually, and they don't always fail when I run them in the suite. I'm assuming there is some sort of issue with threads and how the database is set up, but I can't seem to figure it out.
My spec_helper.rb is a hodgepodge of advice from various sources:
require 'rubygems'
require 'spork'
Spork.prefork do
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
require 'capybara/rspec'
require 'capybara/email/rspec'
include ApplicationHelper
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
include Warden::Test::Helpers
Warden.test_mode!
ActionController::Base.asset_host = "http://localhost:3000"
RSpec.configure do |config|
config.include Rails.application.routes.url_helpers
config.use_transactional_fixtures = false
config.use_transactional_examples = false #factoryGirl
# From http://railsapps.github.io/tutorial-rails-devise-rspec-cucumber.html
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) { DatabaseCleaner.start }
config.after(:each) { DatabaseCleaner.clean }
#
config.infer_base_class_for_anonymous_controllers = false
## Setting up email helpers
config.include MailerMacros
config.before(:each) { reset_email }
## Setting up test and login helpers for Devise testing
config.include Devise::TestHelpers, type: :controller
config.extend ControllerMacros, type: :controller
# From http://railscasts.com/episodes/413-fast-tests
config.order = "random"
config.include FactoryGirl::Syntax::Methods
config.treat_symbols_as_metadata_keys_with_true_values = true
config.filter_run focus: true
config.run_all_when_everything_filtered = true
config.filter_run_excluding :slow unless ENV["SLOW_SPECS"]
config.before(:all) { DeferredGarbageCollection.start }
config.after(:all) { DeferredGarbageCollection.reconsider }
end
end
Spork.each_run do
Rails.cache.clear
Warden.test_reset!
FactoryGirl.reload
end
I've tried removing pieces to troubleshoot, but it's hard to gauge whether it hss solved anything since the errors are sporadic.
Is there anything here that stands out as an obvious reason for the errors above, or any advice on how to troubleshoot this?
In your spec_helper.rb, try setting
config.use_transactional_fixtures = true

Capybara visit not working

ok I not sure what I am missing on the setting up of capybara, but on testing the visit link would appear not to be working. so I have a 3.2 app, I have my specs_helper set up like so:
require "spork"
Spork.prefork do
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'factory_girl'
require 'capybara/rspec'
require 'capybara/rails'
Capybara.javascript_driver = :selenium
Capybara.default_wait_time = 5
include Devise::TestHelpers # see spec/support/devise.rb
include CarrierWave::Test::Matchers
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
include New_spec_helpers
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
config.mock_with :rspec
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = false
config.before :each do
Capybara.default_selector = :css
if Capybara.current_driver == :rack_test
DatabaseCleaner.strategy = :transaction
else
DatabaseCleaner.strategy = :truncation
end
DatabaseCleaner.start
end
config.after do
DatabaseCleaner.clean
end
end
end
Spork.each_run do
FactoryGirl.factories.clear
FactoryGirl.define do
sequence(:code) { |n| "code#{n}" }
sequence(:title) { |n| "title#{n}" }
end
Dir[Rails.root.join("spec/factories/**/*.rb")].each{|f| load f}
PortfolioNew::Application.reload_routes!
end
Then my test set up like this:
before(:each) do
build_projects
visit(projects_path)
end
it "test js" , :js=>true do
current_path.should == projects_path
end
However all i get in return is this:
Failure/Error: current_path.should == projects_path
expected: "/projects"
got: nil (using ==)
i have checked my routes and the projects path is definitely correct and working. Any thoughts on what the issue is would be gratefully received
Ok I found the issue in a comment on the this similar post Rails 3 rspec + capybara - current_path is nil?
Turns out the webrat conflicts with Capybara which was left in when from old tests.
So for anyone interested I just commented out of my gemfile and all good!
Try moving the visit call into the it block:
before(:each) do
build_projects
end
it "test js" , :js=>true do
visit(projects_path)
current_path.should == projects_path
end

Login Failure when Testing with Capybara, Rspec and Selenium in Rails 3.1

I added some confirmation dialog boxes for my Rails 3.1 application and, prior to that, their corresponding tests. Following the model of Railscast #257, I added ':js => true' to the test, added database_cleaner and modified the spec_helper.rb file accordingly.
When I run the test, Firefox launches, Capybara-Selenium fills in the fields the the appropriate username and a password, but log-in fails (i.e., "invalid username/password".) Other tests that do not have ':js => true' and also login, do still pass.
I would like to add more javascript to my application in the future and I am avoiding solutions that would hack Capybara to get this to work (e.g., click 'OK' on all dialogs.)
Any ideas what I might be missing? Fail that, any suggestions on how to debug this problem?
Thank you.
You should set use_transactional_fixtures = false for your seleniumtests.
This can be done in the spec_helper.rb with
config.use_transactional_fixtures = false
for all your tests.
Or do this for a single testcase:
describe 'testcase' , :type => :request do
self.use_transactional_fixtures = false
it 'your test', :js => :true do
testing...
end
end
This happens because selenium-tests access the database in a different way. With transactional fixtures enabled, selenium will work on an empty database -> your user does not exist and you cannot login.
For normal tests you should use transactional fixtures because your tests run much faster.
As per Avdi Grimm's post (features a detailed explanation):
Gemfile:
group :test do
gem 'database_cleaner'
end
spec_helper.rb:
config.use_transactional_fixtures = false
spec/support/database_cleaner.rb:
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, :js => true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
I had transactional fixtures disabled but adding the :js => true block to database_cleaner.rb did it for me.