I have a Rails App, testing with RSpec and Capybara
I have following test:
context 'destroy user' do
scenario "should be successful" do
user = User.create(first_name: 'John', last_name: 'Doe', email: 'john.doe#example.com')
visit users_path
click_link 'Destroy'
# expect { click_link 'Destroy' }.to change(User, :count).by(-1)
expect(page).to have_content 'User was successfully destroyed'
end
end
I have following link in the browser:
<%= link_to "Destroy", user_path(user), method: :delete, data: { confirm: "Are you sure?" } %>
I run the test, and the test passes. It shouldn't pass, since I have the alert window before calling the action.
I installed
brew cask install chromedriver
because I wanted to test JS, but unfortunately test is not failing.
rails_helper.rb
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
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?
# Add additional requires below this line. Rails is not loaded until this point!
require 'rspec/rails'
require 'capybara/rails'
# Capybara.default_driver = :selenium_chrome ### not working
# require "support/factory_bot"
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
begin
ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
puts e.to_s.strip
exit 1
end
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = true
config.infer_spec_type_from_file_location!
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
end
Once the test fails, I would improve the test with:
accept_confirm do
click_link 'Destroy'
end
I think my problem is, I don't know where to put the line:
Capybara.default_driver = :selenium_chrome ### not working
Where do I create the file for the config of Capybara? In the official documentation, they say in the lib/capybara.rb
I tried there, and I get the error:
An error occurred while loading ./spec/models/user_spec.rb.
Failure/Error: require File.expand_path('../../config/environment', __FILE__)
Bundler::GemRequireError:
There was an error while trying to load the gem 'capybara'.
Gem Load Error is: uninitialized constant Capybara
Backtrace for gem load error is:
/Users/albert/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/active_support.rb:74:in `block in load_missing_constant'
/Users/albert/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/active_support.rb:8:in `without_bootsnap_cache'
You don't show your Capybara config (default_driver/javascript_driver), and you also don't have js metadata on your test which would imply you're possibly using the rack_test driver to run your tests. The rack_test driver doesn't support JS and therefore would completely ignore the JS confirm. If you want to test JS behaviors you need to make sure you're using a driver that actually supports JS - see https://github.com/teamcapybara/capybara#drivers and https://github.com/teamcapybara/capybara#using-capybara-with-rspec
Related
I'm trying to use Capybara as a front-end test suite (Vue) and I'm getting the following error from Puma:
Failure/Error: raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
ActionController::RoutingError:
No route matches [GET] "/login"
What is the cause of this and how can I fix this? Is it something to do with the config? Let me know if I should include more code in my question.
rails_helper.rb:
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 "pundit/rspec"
require "capybara/rails"
require "capybara/rspec"
require "capybara/poltergeist"
require 'database_cleaner'
require 'factory_girl'
require 'shoulda/matchers'
require "paperclip/matchers"
require 'support/spec_helpers/warden_controller_helpers'
require 'webmock/rspec'
require 'support/spec_helpers/webmock_helper'
Capybara.javascript_driver = :poltergeist
login_spec.rb:
require 'rails_helper'
RSpec.feature "User login", type: :feature, js: true do
before :each do
#user = User.create!(
first_name: "Bob",
last_name: "Brown",
email: "bob#email.com",
password: "bob1",
has_accepted_terms: true
)
end
scenario "User logs in with correct credentials" do
visit root_path
save_screenshot
click_on 'Log In'
sleep 1
save_screenshot
end
end
So if I run the dev server and go to 'localhost:3000 (which is basically what you're telling Capybara to do) then there is a broken 'Log In' link on the page that points to app.localhost:3000/login which the app doesn't handle. This is because visiting a URL without the app subdomain uses the application.html.haml layout which includes no JS (and therefore no Vue router to handle the /login path). If however I go to app.localhost:3000 in the browser then there's a page with a working Log In link since all the JS is loaded on that page (including the Vue router) because it uses the app.html.haml layout (BaseController)
You need to fix the main homepage link to function correctly and/or configure Capybara to connect to the app subdomain by default
Capybara.app_host = 'http://app.localhost' # hostname with ‘app’ sub domain that resolves to interface the AUT is being run on
Capybara.always_include_port = true
Basically Capybaras telling you your apps main page is broken because it is broken.
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'm using rspec, capybara and launchy to test my web application.
Here's my spec:
require 'spec_helper'
describe "Routes" do
describe "GET requests" do
it "GET /root_path" do
visit root_path
page.should have_content("All of our statuses")
click_link "Post a New Status"
page.should have_content("New status")
fill_in "status_name", with: "Jimmy balooney"
fill_in "status_content", with: "Oh my god I am going insaaaaaaaaane!!!"
click_button "Create Status"
page.should have_content("Status was successfully created.")
click_link "Statuses"
page.should have_content("All of our statuses")
page.should have_content("Jimmy balooney")
page.should have_content("Oh my god I am going insaaaaaaaaane!!! ")
save_and_open_page
end
end
end
My .rspec
--color
--order default
and my spec_helper.rb:
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
require 'capybara/rspec'
# 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 }
# Checks for pending migrations before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
RSpec.configure do |config|
# ## Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.start
DatabaseCleaner.clean
end
config.after(:each) do
DatabaseCleaner.clean
end
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = "random"
end
If you look back at my spec, you'll see a rspec spec that uses capybara to browse my application, and finishes by calling the launchy gem's save_and_open_page method to open this final page in a browser for a human to look at. At this final page, however, there is no javascript or css displayed, just pure HTML.
Does anyone have any ideas why this would be? I want to test javascript, and would prefer it if all assets were loaded.
Inside config.before(:suite) do
add:
%x[bundle exec rake assets:precompile]
to precompile your Rails assets then in your test.rb environment file add:
config.action_controller.asset_host = "file://#{::Rails.root}/public"
config.assets.prefix = 'assets_test'
to point to the location of the precompiled assets. Now you can use assets when you run Capybara. Note: make sure if you are using git to ignore that new folder.
You can just add to test.rb:
config.assets.compile = true
HI i am trying to test a form by filling some data through capybara . My test runs without any error but i can not see that data either in test or development databases. My test is in "spec/feature/FILE NAME". My test is something like
require 'spec_helper'
feature "New Application" do
scenario 'has 200 status code if logged in' do
visit '/applications/new?id=.........'
fill_in 'application[applicants_attributes][0][first_name]', :with => 'Rose'
fill_in 'application[applicants_attributes][0][first_name]', :with => 'Farmer'
click_link 'sbmt'
current_path.should == '/applications/new'
page.status_code.should be 200
end
end
Need Help please!!!!
My spec_helper is something like
require 'simplecov'
SimpleCov.start do
add_filter '/spec/'
add_filter '/config/'
add_filter '/lib/'
add_filter '/vendor/'
end
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
# Add this to load Capybara integration:
require 'capybara/rspec'
require 'capybara/rails'
require 'rspec/autorun'
require 'crack'
Capybara.register_driver :rack_test do |app|
Capybara::RackTest::Driver.new(app, :headers => { 'User-Agent' => 'Capybara' })
end
# 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 Capybara::DSL, type: :feature
RSpec.configure do |config|
config.use_transactional_fixtures = false
#config.use_transactional_fixtures = false
config.before :each do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.start
end
config.after do
DatabaseCleaner.clean
end
end
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = "random"
end
My rspec output is
Failures:
1) New Application has 200 status code if logged in
Failure/Error: Application.where("first_name = 'Rose' AND last_name = 'Farmer'").count.should == 1
expected: 1
got: 0 (using ==)
# ./spec/features/applications_controller_spec.rb:23:in `block (2 levels) in <top (required)>'
Finished in 0.7381 seconds
1 example, 1 failure
Failed examples:
rspec ./spec/features/applications_controller_spec.rb:4 # New Application has 200 status code if logged in
Randomized with seed 49732
Coverage report generated for Cucumber Features to /home/nomi/homesbyhaven/coverage. 241 / 616 LOC (39.12%) covered.
Problem is i am not able to save the data to database . if i remove the check for the data in Application table from the test. it passes. but how can i verify that it is really passing. i mean to say there is no issues.
Thanks
I think the issue here is that you are testing the wrong thing. A feature (integration) spec tests the system the way a user interacts with it. So as much as possible, your test should limit itself to the activities a user can perform. Instead of checking that, for example, after adding a user the User.count goes up by one, have the test do the same thing a user would do to verify that the the action was successful. You could visit the user page to see that the user was added, or have an element on the page that tells you how many users exist.
Remember that the test server and the browser use separate processes. This can cause timing issues where you expect that the server has completed an action (eg. adding a user) in one process, but the database change is occurring in another process. You can avoid those issues by having the test browser imitate a user's actions. Do the database testing in model specs.
By default the test database is cleared after each test. So that each time you run the tests, you've got a clean slate.
If you don't want this to happen you can change the config in you spec/spec_helper.rb to
config.use_transactional_fixtures = false
but if you not clearing the db after each test, then any records created from the previously run test could change the result of the next test. Giving you a false positive or negative
You will have a block like below in your spec/spec_helper.rb . I do not recommend disabling the transactional fixture as it will cause lot of problems when you have more than one test.
RSpec.configure do |config|
# Mock Framework
config.mock_with :rspec
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# Render views when testing controllers
# This gives us some coverages of views
# even when we aren't testing them in isolation
config.render_views
end
Instead what you can do is, make the database check as an assertion in your test case
require 'spec_helper'
feature "New Application" do
scenario 'has 200 status code if logged in' do
visit '/applications/new?id=.........'
fill_in 'application[applicants_attributes][0][first_name]', :with => 'Rose'
fill_in 'application[applicants_attributes][0][last_name]', :with => 'Farmer'
click_link 'sbmt'
current_path.should == '/applications/new'
Application.where("first_name = 'Rose' AND last_name = 'Farmer'").count.should == 1
page.status_code.should be 200
end
end
If the above test fails, then your functionality is not working as expected
You cannot use transactional fixtures if you are using anything other than ActiveRecord.
If you are using MongoID you can use Database Cleaner with Truncation Strategy
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.before :each do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.start
end
config.after do
DatabaseCleaner.clean
end
end
So I've been struggling with this for quite some time now and I can't seem to figure our what's going wrong, and couldn't find much on what could possibly cause this issue.
I'm relatively new to Ruby and Rails, as well as test/behavior driven development and am trying to write some acceptance (browser) tests using PhantomJS through Poltergeist, using Rspec and Capybara. I believe some people also call this integration tests (they may be from some perspective), but that's a whole other discussion.
I have a really simple feature that I can't get to do what I want:
require 'feature_helper'
feature 'Logging in', :js => true do
scenario 'with incorrect credentials' do
visit '/login'
puts page.html
save_and_open_page
page.driver.render('_screenshot.png', :full => true)
page.html.should have_selector("title", :text => "hi")
end
end
So. Simple, right. It should just go to /login and throw the HTML content at me, as well I want to see the page using save_and_open_page, and I want it to take a screenshot. I added a simple should have_selector in order to have the test fail in an attempt to get more feedback.
The relative contents of my feature_helper.rb:
require 'spec_helper'
require 'capybara/rspec'
require 'capybara/rails'
require 'capybara/poltergeist'
include Capybara::DSL
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, {
:debug => true,
:inspector => true
})
end
Capybara.default_driver = :poltergeist
Capybara.javascript_driver = :poltergeist
FakeWeb.allow_net_connect = %r[^https?://(127.0.0.1|localhost)] # allow phantomjs/poltergeist requests
DatabaseCleaner.strategy = :truncation
RSpec.configure do |config|
config.before :each do
# Set the hostname to something with test
#host = "test.iome:3003"
host! #host
Capybara.default_host = Capybara.app_host = "http://#{#host}/"
Capybara.server_port = 3003
Capybara.reset_sessions!
# Start the database cleaner
config.use_transactional_fixtures = false
DatabaseCleaner.start
end
config.after :each do
DatabaseCleaner.clean
end
end
And also my spec_helper.rb:
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'pry'
require 'fakeweb'
FakeWeb.allow_net_connect = false
It's all fairly simple.
Now, in my console I see the following:
{"name"=>"visit", "args"=>["http://test.iome:3003//login"]}
{"response"=>{"status"=>"fail"}}
{"name"=>"body", "args"=>[]}
{"response"=>"<html><head></head><body></body></html>"}
<html><head></head><body></body></html>
{"name"=>"body", "args"=>[]}
{"response"=>"<html><head></head><body></body></html>"}
{"name"=>"render", "args"=>["_screenshot.png", true]}
{"response"=>true}
{"name"=>"body", "args"=>[]}
{"response"=>"<html><head></head><body></body></html>"}
Also, the screenshot is just a white and empty page. When I tail my log/test.log file, I don't see that a request is being performed. I've tried changing the method visit to get, and that'll make the request, but won't change any of the results.
I've completely run out of ideas of what this could be and it's rather frustrating :(
Final information then about versions:
rspec 2.10.0
capybara 1.1.4
poltergeist 1.0.3
ruby 1.8.7
rails 3.2.13
Unfortunately we're still at ruby 1.8.7, but are working on bumping that version up. Still, I think this shouldn't influence the tests.
Any help would be greatly appreciated!
So eventually I got help from a colleague, and we managed to fix it. We used the lvh.me domain for this, as any request to that domain will resolve in localhost, allowing you to use subdomains without a problem. You could probably also use hostname.127.0.0.1.xip.io for this.
Our spec_helper.rb now looks like this:
# Use capybara in combination with poltergeist for integration tests
require 'capybara/rails'
require 'capybara/rspec'
require 'capybara/poltergeist'
require 'rack_session_access/capybara'
Capybara.default_driver = :poltergeist
Capybara.always_include_port = true
Capybara.app_host = 'http://application-test.lvh.me' # Any lvh.me domain resolves to localhost
Capybara.default_wait_time = 8 # How long capybara should look for html elements
require 'vcr'
VCR.configure do |config|
config.cassette_library_dir = 'spec/vcr_cassettes'
config.hook_into :fakeweb
config.ignore_localhost = true
config.configure_rspec_metadata!
config.ignore_hosts 'codeclimate.com'
end
require 'fakeweb'
FakeWeb.allow_net_connect = false
Because we hooked in VCR to record any requests going out during the first run of the integration tests, all your integration tests, or features, should contain this code:
before(:all) do
FakeWeb.allow_net_connect = true
end
after(:all) do
FakeWeb.allow_net_connect = false
end
If you want to change the subdomain during your specs, you can use the following:
before(:each) do
#original_host = Capybara.app_host
Capybara.app_host = 'http://does-not-exist.lvh.me'
visit '/login'
end
after(:each) do
Capybara.app_host = #original_host
end
Making screenshots can now be done using page.save_screenshot during specs. Hope this helps.