I can't get Capybara to hold a session beyond one page visit within one spec. I am familiar with the concept that Capybara's driver might be using a different database connection, but anything I do (including not using :js => true) doesn't seem to change anything.
I am just trying to visit a page that requires user authentication. It succeeds on first visit. It fails when I visit it a second time. (This of course all works when I manually test in development.) Code:
activity_areas_spec.rb
require "rails_helper"
RSpec.feature "Activity areas", :type => :feature do
user = FactoryGirl.create(:user)
before(:each) do
login_as(user, :scope => :user)
end
after(:each) do
Warden.test_reset!
end
...
feature "displays activities shared", :js => true do
scenario "to public" do
visit area_activities_path
expect(page).to have_content I18n.t('pages.activity.areas.title') #passes
visit area_activities_path
expect(page).to have_content I18n.t('pages.activity.areas.title') #fails -- user is redirected to login page.
end
end
end
#rails_helper.rb
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
require 'spec_helper'
require 'capybara/rails'
require 'devise'
require 'support/controller_macros'
include Warden::Test::Helpers
Warden.test_mode!
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
# Factory girl
config.include FactoryGirl::Syntax::Methods
# Make routes available in specs
config.include Rails.application.routes.url_helpers
# Capybara
config.include Capybara::DSL
# To get Devise test helpers
config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros::DeviseHelper, :type => :controller
config.include ControllerMacros::AttributeHelper, :type => :controller
config.use_transactional_fixtures = false
config.infer_spec_type_from_file_location!
config.filter_rails_from_backtrace!
# Database cleaner gem configurations
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
Since I was sensitive to the fact that Capy might be using a different database connection, I have tried following DatabaseCleaner's suggested code to the tee in the place of the above DatabaseCleaner commands:
config.before(:suite) do
if config.use_transactional_fixtures?
raise(<<-MSG)
Delete line `config.use_transactional_fixtures = true` from rails_helper.rb
(or set it to false) to prevent uncommitted transactions being used in
JavaScript-dependent specs.
During testing, the app-under-test that the browser driver connects to
uses a different database connection to the database connection used by
the spec. The app's database connection would not be able to access
uncommitted transaction data setup over the spec's database connection.
MSG
end
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, type: :feature) do
driver_shares_db_connection_with_specs = Capybara.current_driver == :rack_test
if !driver_shares_db_connection_with_specs
DatabaseCleaner.strategy = :truncation
end
end
config.before(:each) do
DatabaseCleaner.start
end
config.append_after(:each) do
DatabaseCleaner.clean
end
See here.
#spec_helper.rb
require 'factory_girl_rails'
require "capybara/rspec"
require 'sidekiq/testing'
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
end
I'm confused easily, so much appreciated for any help that is provided in simple terms. And let me know if you need any other files.
As a quick guess your user = FactoryGirl.create(:user) needs to be inside your before block - otherwise it would only be run once when the feature is loaded - and then removed from the database before the first feature is run
Related
I have some services inside my app/services folder.Now I want to test these services, so I am using rspec and factorygirl. I am creating user with factoryGirl, but when i am running rspec it is showing me following error
Failure/Error: #user ||= FactoryGirl.create(:user)
ActiveRecord::RecordInvalid:
Validation failed: Email has already been taken
I am using database_cleaner gem to clean the database after each test.
Below is my config file
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'spec_helper'
require 'rspec/rails'
require 'factory_girl_rails'
require 'database_cleaner
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.include Devise::TestHelpers, type: :controller
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
config.infer_spec_type_from_file_location!
config.filter_rails_from_backtrace!
end
I don't know where i am going wrong.
You need to specify the strategy to use between each specs (transaction is a safe choice here):
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
I have an RSpec feature spec that tests the login for my app. It passes when I run it in RSpec with Capybara, but when I try to run it flagged with js: true using Capybara-webkit, it fails. This is a problem because my entire app is behind the login, and if I can't get this bit to run I don't know how to do feature specs for the rest of the app.
Here's what I have tried:
Installing all the Capybara-webkit dependencies listed here. I'm running my app in a Docker container built on the ruby:2.3 image, which is built on Jessie.
Setting up DatabaseCleaner per this blog post. My database_cleaner.rb file is below.
Using the Headless gem (headless.rb below)
Running RSpec like so: xvfb-run -a bin/rspec spec/features/log_in_spec.rb (seems no different than running it normally with Headless)
How do I get my login specs to work under Capybara-webkit? Some of my specs will need to be flagged for JS and some won't, but they'll all need the user to be logged in. Thank you.
log_in_spec.rb
require 'rails_helper'
RSpec.feature "Log in", type: :feature do
scenario "as admin" do
user = create(:admin)
# Tried this instead of with Capybara, works with Capybara but not capybara-webkit
# login_as user, scope: :user, run_callbacks: false
visit root_path
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
find('.btn-primary').click
expect(page).to have_content('Admin')
end
end
spec_helper.rb
require 'capybara/rspec'
require 'paperclip/matchers'
RSpec.configure do |config|
Capybara.javascript_driver = :webkit
Capybara.app_host = 'https://192.168.99.101'
config.include Paperclip::Shoulda::Matchers
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
mocks.verify_doubled_constant_names = true
end
config.filter_run :focus
config.run_all_when_everything_filtered = true
config.disable_monkey_patching!
if config.files_to_run.one?
config.default_formatter = 'doc'
end
end
rails_helper.rb
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'spec_helper'
require 'rspec/rails'
require 'capybara/rails'
require 'devise'
require 'support/controller_macros'
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
# Checks for pending migrations before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.filter_rails_from_backtrace!
config.include Warden::Test::Helpers
config.before :suite do
Warden.test_mode!
end
config.after :each do
Warden.test_reset!
end
end
# Added headless gem and this code thanks to this post: http://stackoverflow.com/a/28706535/3043668
if ENV['HEADLESS']
require 'headless'
headless = Headless.new
headless.start
at_exit { headless.stop }
end
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
spec/support/headless.rb
RSpec.configure do |config|
config.around type: :feature do |example|
Headless.ly do
example.run
end
end
end
spec/support/capybara.rb
Capybara::Webkit.configure do |config|
config.debug = true
config.allow_unknown_urls
config.timeout = 5
config.ignore_ssl_errors
config.skip_image_loading
end
Here is a gist of the debug output from Capybara-webkit when I run the test. It looks like it's trying the same thing over and over.
UPDATE
I removed my Capybara.app_host setting, and the non-JS test still passes, but when I run it under capybara-webkit, I see this in the debig output:
Received 0 from "https://127.0.0.1:37193/login"
Page finished with false
Load finished
Page load from command finished
Wrote response false "{"class":"InvalidResponseError","message":"Unable to load URL: http://127.0.0.1:37193/login because of error loading https://127.0.0.1:37193/login: Unknown error"}"
Received "Reset()"
Started "Reset()"
undefined|1|SecurityError: DOM Exception 18: An attempt was made to break through the security policy of the user agent.
It's trying to visit("/login") and it is being redirected to the https version, and this is making it fail. How do I make it succeed?
The first reason is that the record after save procedure c=keep not the password in plain view, but seems that it is good in this case:
user = create(:admin)
# ...
user = User.first # wrong way
#...
fill_in 'Password', with: 'password'
The second reason in fail to login is that factory girl and capybara uses separate connections, so and data created in one session isn't available in another. To fix it use single connection patch (put it to spec/support) as described here.
This was a hard one. But the issue was that I had set force_ssl = true in my application.rb, stupidly, instead of putting it in production.rb and development.rb like a normal person.
I also had set Capybara-webkit's app_host, which, as it turned out, I did not need to do. After removing that, and running capybara-webkit with debug on, I saw that it was trying to redirect from http://localhost:45362/login (or whatever port) to https://localhost:45362/login (note the https!) and that this was causing a DOM 18 security error or whatever, and this was making it choke. I turned off force_ssl and now it works like a champ. Hope this helps you not tear your hair out.
I have a simple scenario in my rspec feature:
require 'spec_helper'
feature 'CLIENT views results page' do
context 'from welcome/index form' do
let!(:location) { FactoryGirl.create :location, name: 'town' }
before :each do
visit '/'
end
scenario 'successfully', js: true do
expect(Location.count).to eq 1
fill_in 'from_address', with: 'Some address'
fill_in 'to_address', with: 'Another address'
click_button 'Search'
expect(page).to have_content 'Some address → Another address'
end
end
end
spec_helper:
require 'rubygems'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'factory_girl'
require 'database_cleaner'
require 'capybara/poltergeist'
FactoryGirl.reload
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.infer_base_class_for_anonymous_controllers = true
config.order = "random"
config.tty = true
config.mock_with :rspec
config.filter_run focus: true
config.run_all_when_everything_filtered = true
config.treat_symbols_as_metadata_keys_with_true_values = true
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
Capybara.javascript_driver = :poltergeist
Capybara.asset_host = "http://localhost:3000"
Capybara.server_port = 9887 + ENV['TEST_ENV_NUMBER'].to_i
And my scenario fails on some random step (after expect(Location.count).to eq 1) due to the fact, that there are no Location records:
(byebug) Location.all
[]
I think it might be one of two cases:
Either DatabaseCleaner cleans database before an example finishes
Or there are actually two threads with two databases, one of which (the one that is used by the app) is really empty
Gem versions:
rspec (2.14.1)
rspec-core (2.14.8)
rspec-rails (2.14.0)
phantomjs (1.9.7.1)
rails (3.2.21)
database_cleaner (0.9.1)
I remember having issues with this as well. My config for DB cleaning looks like this:
config.before(:each) do
if Capybara.current_driver == :rack_test
DatabaseCleaner.strategy = :transaction
else
DatabaseCleaner.strategy = :truncation
end
DatabaseCleaner.start
end
config.after do
DatabaseCleaner.clean
end
Maybe it has something to do with the order your before blocks are called. So if DatabaseCleaner.start is called before you wanted strategy is set, you might see such results.
Managed to solve this issue by migrating to
config.use_transactional_fixtures = true
instead of using DatabaseCleaner. Some workarounds are suggested to make transactional cleaning work with javascript tests. I chose this one:
(adding somewhere in the beginning of spec/spec_helper)
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || retrieve_connection
end
end
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
cause it seemed to work faster, still leaving some spontaneously failing tests (might not be the problem related to database).
I have an odd problem and I'm not sure how to debug it. I'm currently using these gems (which are the latest versions as of this post):
factory_girl-4.5.0
rspec-rails-3.1.0
capybara-2.4.4
guard-2.6.1
Whenever I start guard using bundle exec guard or whenever a new test runs, it always create a user in the test database. So that User.count == 1 at every start. I can't figure out where that's called.
Here's my spec_helper.rb:
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'email_spec'
# require 'rspec/autorun'
require 'capybara/rspec'
require 'factory_girl'
require 'lorem-ipsum'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
config.include(EmailSpec::Helpers)
config.include(EmailSpec::Matchers)
config.include FactoryGirl::Syntax::Methods
config.include ApplicationHelper
config.use_transactional_fixtures = false
config.infer_base_class_for_anonymous_controllers = false
config.include RSpec::Rails::RequestExampleGroup, type: :feature
config.infer_spec_type_from_file_location!
config.include Requests::JsonHelpers, :type => :request
config.order = "random"
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
Capybara.javascript_driver = :webkit
end
config.before(:each) do
Mongoid::IdentityMap.clear #If you have the identity map enabled in your application, you should set up a global hook to clear out the map before each test so the test suite does not create memory bloat. For example in RSpec in spec_helper.rb.
Rails.cache.clear
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
and my factories.rb is pretty simple:
FactoryGirl.define do
factory :user, aliases: [:authored_by] do
sequence(:first_name) {|n| "TestFN#{n}" }
sequence(:last_name) {|n| "TestLN#{n}" }
sequence(:email) {|n| "rspec-test-#{n}#example.com" }
end
end
Any idea on where I should start debugging to figure out why the user is being created?
Look in your /spec directory. Is FactoryGirl.lint being called? Are you calling the user factory in a test defined in spec/features, etc? Are you using DatabaseCleaner to maintain your test environment?
I'm using rspec to perform feature tests and I can't save the user in the DB before the log in.
I'm using factory girl to build the object.
fixture are saved in db at the beginning of the test but are not deleted at the end.(maybe because the test fail. I don't know)
So I can not save the user before clicking on logIn and I get this errror message
--
DEPRECATION WARNING: an empty resource was given to Devise::Strategies::DatabaseAuthenticatable#validate. Please ensure the resource is not nil. (called from set_required_vars at app/controllers/application_controller.rb:43)
spec/features/login_to_mainpage_spec.rb (no error are rescued)
require "rails_helper"
feature 'Navigating to homepage' do
let(:user) { create(:user) }
let(:login_page) { MainLoginPage.new }
scenario "login" do
login_page.visit_page.login(user)
sleep(20)
end
end
A simple page object: spec/features/pages_objects/main_login_page.rb
class MainLoginPage
include Capybara::DSL
def login(user)
fill_in 'email', with: user.email
fill_in 'password', with: "password"
click_on 'logIn'
end
def visit_page
visit '/'
self
end
end
my rails_helper
require 'spec_helper'
require 'capybara/rspec'
require 'capybara/poltergeist'
require "selenium-webdriver"
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
Dir[Rails.root.join("spec/features/page_objects/**/*.rb")].each { |f| require f }
RSpec.configure do |config|
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = false
config.before :each do
DatabaseCleaner.start
end
config.after :each do
DatabaseCleaner.clean
end
config.infer_spec_type_from_file_location!
config.include Devise::TestHelpers, :type => :controller
end
Capybara.default_driver = :selenium
Capybara.register_driver :selenium do |app|
Capybara::Selenium::Driver.new(app, :browser => :firefox)
end
in spec helper:
require 'simplecov'
require 'factory_girl'
require 'rspec/autorun'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
ENV["RAILS_ENV"] ||= 'test'
RSpec.configure do |config|
include ActionDispatch::TestProcess
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.disable_monkey_patching!
if config.files_to_run.one?
config.default_formatter = 'doc'
end
config.profile_examples = 10
config.order = :random
Kernel.srand config.seed
end
EDIT 1
I switch "gem 'factory_girl'" to "gem 'factory_girl_rails'"
and add this to application.rb
config.generators do
|g|
g.test_framework :rspec,
:fixtures => true,
:view_specs => false,
:helper_specs => false,
:routing_specs => false,
:controller_specs => true,
:request_specs => true
g.fixture_replacement :factory_girl, :dir => "spec/factories"
end
I still can not save the user in DB.
Everything pass but I put some sleep(10) in by code to refresh my DB and see te records and user is never saved
EDIT 3
My problem is actually very simple. FactoryGirl.create never save the data in DB if I put in my rails_helper:
config.use_transactional_fixtures = true
or
config.before :each do
DatabaseCleaner.start
end
config.after :each do
DatabaseCleaner.clean
end
/spec/factories
FactoryGirl.define do
factory :user do
email 'pierre#tralala.com'
password 'password'
password_confirmation 'password'
end
/spec/support/factory_girl.rb
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
end
spec/features/login_to_mainpage_spec.rb
let(:user) { create(:user) }
scenario "login" do
login_page.visit_page.login(create(:user))
sleep(5)
end
User will not be saved because of the config I cited before.
I need to have data reseted between tests.
EDIT 4
If I'm using the console
RAILS_ENV=test rails c
FactoryGirl.create(:user) it is saved in db.
I don't understand why it does not work in my tests.
Try using let! to create your user.
From the rSpec documentation:
Note that let is lazy-evaluated: it is not evaluated until the first time
the method it defines is invoked. You can use let! to force the method's
invocation before each example.
You said that you are using FactoryGirl but I do not see this in your test. To create my Users with FactoryGirl I always do something like this:
FactoryGirl.create(:user, password: 'test', password_confirmation: 'test', name: 'test')
If setup correctly in your Factory you can just write:
FactoryGirl.create(:user)
To solve your problem with the unique fields FactoryGirl provides you sequences:
sequence :email do |n|
"person#{n}#example.com"
end
factory :user do
email { generate(:email }
end
This will start with "person1#example.com" and add 1 to the number each time FactoryGirl.create(:user) is called.