Ive setup capybara-webkit for my integration tests and Im running into a very simple problem. My session is not being stored. The use case is pretty simple
1. Login
2. Go to a specific page
3. Check if it has the approp content
Now at step 2 my app is returning the test case to the login page - which means the session is not being set properly.
any help is much appreciated
If I use #culerity instead of #javascript then this test case passes so the problem seems to be the capybara-webkit setup
My env.rb for capybara-webkit support is as follows
Spork.prefork do
require 'cucumber/rails'
require 'capybara'
require 'capybara/dsl'
require 'capybara/cucumber'
require 'capybara-webkit'
Capybara.run_server = false
Capybara.javascript_driver = :webkit
Capybara.default_selector = :css
# Capybara defaults to XPath selectors rather than Webrat's default of CSS3. In
# order to ease the transition to Capybara we set the default here. If you'd
# prefer to use XPath just remove this line and adjust any selectors in your
# steps to use the XPath syntax.
# Capybara.default_host = "127.0.0.1:3000"
Capybara.app_host = "http://localhost:3000"
end
Update 1:
Looks like the sessions is being set. I used the following code to dump the session in my steps
puts(Capybara.current_session.driver.browser.get_cookies)
and I got the follwoing - so looks like cookie is being set but not being sent back
["_jqt_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFRiIlYmMwYzNjYjY0MGU3NTg0OWFlNTcwODhmM2I2MzE1YmRJIhBfY3NyZl90b2tlbgY7AEZJIjEwRzN6NG1NTzZqamNCNC9FdWZWeXBCMHdoeThueXBnaTJDcTVzbmJqQlBZPQY7AEZJIgpmbGFzaAY7AEZJQzolQWN0aW9uRGlzcGF0Y2g6OkZsYXNoOjpGbGFzaEhhc2h7BjoKYWxlcnRJIh9JbnZhbGlkIGVtYWlsIG9yIHBhc3N3b3JkLgY7AFQGOgpAdXNlZG86CFNldAY6CkBoYXNoewY7B1Q%3D--3fbe1c2a77a433228e7b7f2d8c8f0aec3ad5fb5f; HttpOnly; domain=localhost; path=/"]
Update 2:
was barking up the wrong tree. Seems that the user I was creating in my test case was not being seen by the rails app as my database cleaner strategy was set to transactional. see more info at
https://groups.google.com/forum/#!msg/ruby-capybara/JI6JrirL9gM/R6YiXj4gi_UJ
To add more clarity,
Capybara webkit or selenium driver is running in a different thread then app, so if you are using transactional fixtures or database_cleaner with strategy :transaction and your data isn't commited to db and another thread won't see it.
Possible solutions are:
Use database_cleaner with strategy :truncation. (solid, but a slow)
Add code to force active record using single transaction for all threads. (faster, but may have some issues, ex.: after_commit hooks aren't called, as there's no commit)
#Capybara use the same connection
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
I'm using 2-nd option, but it's arguable.
Related
I'm starting in the automated testing area and am having the error below when I am running one of my tests. The test would be to fill in a datetime field, of type input, with the start time that the machine stopped. I'm running Cucumber with Capybara on a system written in Ruby on Rails and Ext Js.
Error: wrong number of arguments (given 1, expected 0) (ArgumentError)
Bellow is my env.rb for the project:
require 'capybara'
require 'capybara/cucumber'
require 'report_builder'
require 'selenium-webdriver'
require 'webdrivers/chromedriver'
Capybara.javascript_driver = :webkit
Capybara.configure do |config|
config.default_driver = :selenium_chrome
config.default_max_wait_time=10
config.app_host='http://localhost:3000/'
end
My exemple.feature is like:
Scenario: start of the machine stop
Given that I accessed the system
And marked 'Yes' in 'Did the machine stop?'
And I fill the start of the machine stop with '2020-05-12 16:00:00' #UTC
When saving the form
Then the success message should appear
My steps.rb is something like below:
And("I fill the start of the machine stop with {string}") do
#machine_stop = machine_stop
#MD.machine_stop_start(#machine_stop)
end
My page.rb:
class Machine_downtime
include Capybara::DSL
def machine_stop_start
find('input[id="datetimefield-1312-inputEl"]').click
end
end
I've done a Google search to find possible solutions but I haven't found any similar cases. I tried to look at the Cucumber documentation but I didn't find anything specific to my problem or Rails.
I can stop using a strings in the step and use a table, but I have more scenarios in this case and I would like to continue using strings, for easier maintenance and to avoid having to change more than 100 test scenarios.
I appreciate anyone who can help me with the problem.
P.s. Ruby on Rails runs on a different project.
Update:
I insert in the machine_stop_start method the variable I created in the step and the error no longer occurs.
class Machine_downtime
include Capybara::DSL
def machine_stop_start (machine_stop)
find('input[id="datetimefield-1312-inputEl"]').click
end
end
Now I am able to click on the desired field but it is not being filled with the desired information.
I will try to find what I may be doing wrong.
So im running SQLite and attempting to use fixtures to load in the proper data to display and test through the browser with capybara.
My test suite uses a Minitest w/Capybara and Poltergeist for the driver. the relevant portion of my test_helper.rb file looks like so:
require "minitest/reporters"
reporters = []
reporters << Minitest::Reporters::SpecReporter.new
Minitest::Reporters.use! reporters, ENV, Minitest.backtrace_filter
require "minitest/rails/capybara"
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, :js_errors => false)
end
Capybara.default_driver = :poltergeist
Capybara.current_driver = :poltergeist
Capybara.javascript_driver = :poltergeist
However I have a simple test that stubs a User access level login for the web application this is running on, and then simply visits the route.
However it's failing at what seems to be the "User Access Stubbing" method that im using. Which looks like Core::User.any_instance.stubs(etc...) which is just returning a user model.
Anyways the exact error I get is:
ActiveRecord::StatementInvalid: ActiveRecord::StatementInvalid: SQLite3::BusyException: database is locked: commit transaction
Since im using fixtures would this maybe be a DB cleaner issue(Im not using it right now as im only using pre-created fixtures currently) I've never used DB cleaner with minitest as im only familiar with using it with rspec and factory girl.
The error is telling you that another DB connection already has sqlite open for writing (it can handle multiple readers but only one writer at a time). You don't show your actual test or your stub so it's impossible to say specifically where your issue is, but odds are you've made a request and then are running some other database access while it's occurring (for instance have you preloaded the object the stub returns, or does it get loaded when the stub is executed?).
If your app doesn't use sqlite as it's database then swap over to testing on the DB the app will use in production. Additionally, stubbing is an anti-pattern in feature tests, you should just be creating proper records, and dealing with them.
Assuming I have PhantomJS installed on Mac OS X, can I write a plain old Ruby script (no Cucumber, no RSpec) to drive Poltergeist? In other words, I want to require some gems, set the Capybara driver to Poltergeist and then start calling page.fn() to pull down pages, analyze contents, etc?
UPDATE: Here is what I have created. It seems to work.
require 'awesome_print'
require 'capybara'
require 'capybara/dsl'
require 'capybara/poltergeist'
Capybara.javascript_driver = :poltergeist
Capybara.run_server = false
Capybara.app_host = 'http://www.google.com'
Capybara.current_driver = :poltergeist
include Capybara::DSL
visit '/'
page.driver.network_traffic.each do |request|
ap request.response_parts
end
I know the naked include is bad, but I am just hacking at the moment before I encapsulate this in a class.
Feedback?
Yep, you can definitely use capybara & poltergeist without cucumber or rspec. What you have looks like a pretty good start.
However, you will probably want to use some standardized testing library to provide you with assertion methods & a test runner so you don't have to recreate all that stuff yourself. Your primary options are:
test/unit (now minitest under the hood)
minitest/spec (a spec-like wrapper for minitest)
rspec
Here are the capybara integration instructions for each:
test/unit
minitest/spec
rspec
Yes, I know I shouldn't be, but I just need to run a little bit of Capybara script in a controller.
The problem is it opens up an browser and everything, however I want to complete shut down the test or run it in isolation.
So for example if I run something like:
Capybara.current_driver = :selenium
Capybara.app_host = 'https://www.google.com'
Capybara.visit("/")
Capybara.has_content?('foo')
Capybara.reset_sessions!
The browser and session is still left open.
If I close the browser down and re-run the test it throws an "Connection refused - connect(2)" exception.
I have to re-start the whole rails app to be able to re-run the test.
This there a way to run and re-run several Capybara test over and over again without have to restart anything?
Something hypothetical like this would be good:
Capybara.new do
#the tests...
end
or this at the end… Capybara.shutdown
I can't seem to find anything in the docs.
Capybara's selenium driver has a quit method. When Capybara starts a browser it registers at_exit hook that will invoke quit.
But as you want to quit it itself you should do two things:
Invoke Capybara.page.driver.quit yourself
Monkeypatch Capybara so that you won't get errors when Capybara tries to close browser when you have already closed it:
# from https://github.com/jnicklas/capybara/blob/master/lib/capybara/selenium/driver.rb#L9
# code inside at_exit hook is removed
class Capybara::Selenium::Driver
def browser
unless #browser
#browser = Selenium::WebDriver.for(options[:browser], options.reject { |key,val| SPECIAL_OPTIONS.include?(key) })
end
#browser
end
end
Here is a POC code - https://gist.github.com/abotalov/6274926
As you need just "a few Capybara commands" you can also write a helper method if you want:
def with_capybara(&block)
Capybara.app_host = 'http://www.stackoverflow.com'
session = Capybara::Session.new(:selenium)
block.call(session)
session.driver.browser.quit
end
Here is a POC code too - https://gist.github.com/abotalov/6274999
I can't say I understand why you are trying to do this... However here are a couple of things to try.
From: https://github.com/jnicklas/capybara
Transactions and database setup
Some Capybara drivers need to run
against an actual HTTP server. Capybara takes care of this and starts
one for you in the same process as your test, but on another thread.
Selenium is one of those drivers, whereas RackTest is not.
Given that, when using Selenium, Capybara needs to start an actual HTTP server, you should try an alternative driver like RackTest. Capybara may not understand you need the HTTP server shutdown.
Also, from that same section:
It is also possible to force your ORM to use the same transaction for
all threads. This may have thread safety implications and could cause
strange failures, so use caution with this approach. It can be
implemented in ActiveRecord through the following monkey patch:
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
You may try the provided patch to see if Capybara is initiating threads that are not being shutdown, too.
In either case, I have no idea whether these options will help you, but it's something to try.
I have integration tests that are intermittently failing, and always with ActiveRecord::RecordNotFound errors. The error takes place inside the controller where a find call takes place given an ID from a fixture. It takes place in multiple controllers, though. I never see this behaviour while navigating the site, but I'd say the tests fail about 30-50% of the time. Running the test again after a failure seems to fix the problem.
If I load the fixtures manually into the development database, the ID that doesn't seem to be found is indeed present inside the tables.
I haven't been able to find much info on people having the same problem... any ideas?
UPDATE: Here are the contents of test_helper.rb
ENV["RAILS_ENV"] = "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'capybara/rails'
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
#
# Note: You'll currently still have to declare fixtures explicitly in integration tests
# -- they do not yet inherit this setting
fixtures :all
# Add more helper methods to be used by all tests here...
end
# Transactional fixtures do not work with Selenium tests, because Capybara
# uses a separate server thread, which the transactions would be hidden
# from. We hence use DatabaseCleaner to truncate our test database.
DatabaseCleaner.strategy = :truncation
class ActionDispatch::IntegrationTest
# Make the Capybara DSL available in all integration tests
include Capybara::DSL
# Make the Capybara Email DSL available in all integration tests
include Capybara::Email::DSL
# Stop ActiveRecord from wrapping tests in transactions
self.use_transactional_fixtures = false
# Switch to selenium as the default driver for JS support
Capybara.default_driver = :selenium
# Only click on visible links!
Capybara.ignore_hidden_elements = true
teardown do
DatabaseCleaner.clean # Truncate the database
Capybara.reset_sessions! # Forget the (simulated) browser state
Capybara.use_default_driver # Revert Capybara.current_driver to Capybara.default_driver
end
end
UPDATE Here are the results of running the same test 5 times in a row. I ran the tests, waited until they finished, and then immediately ran them again with the command rails test:integration. NOTE: The E and F that are consistent throughout the tests are actually test errors -- I'm working on fixing those. For example, the second test run-through was "correct", but the first showed the spurious error.
..E......E..........F.
.........E..........F.
..E......E..........F.
..E......E..........F.
..E....E.E..........F.
The errors do occur across two separate tables -- they're not trying to find the same record. But it does appear to be only a subset of the tests that have this issue...
UPDATE Here's what the actual error looks like in the test results:
1) Error:
test_browsing_user_snops(BrowseStoriesTest):
ActiveRecord::RecordNotFound: Couldn't find User with id=980190962
/home/myuser/.rvm/gems/ruby-1.9.3-p286/gems/activerecord-3.2.9/lib/active_record/relation/finder_methods.rb:341:in `find_one'
/home/myuser/.rvm/gems/ruby-1.9.3-p286/gems/activerecord-3.2.9/lib/active_record/relation/finder_methods.rb:312:in `find_with_ids'
/home/myuser/.rvm/gems/ruby-1.9.3-p286/gems/activerecord-3.2.9/lib/active_record/relation/finder_methods.rb:107:in `find'
/home/myuser/.rvm/gems/ruby-1.9.3-p286/gems/activerecord-3.2.9/lib/active_record/querying.rb:5:in `find'
/home/myuser/Projects/myproject/app/controllers/users_controller.rb:18:in `show'
...
Notice that the error appears when the controller tries to find a record. The relevant line is actually #user = User.find(params[:id]). It occurs with other models also, not just the users controller and not just the User model.
I'm concerned that there may be a delay during the truncation of data and the speed in which Capybara is driving the web browser which sometimes may result in identity column values starting at an unexpected value (i.e., 7 for record #1 - when you expect 1, because that identity generator has not yet been reset). I have no evidence that this is the case but its my best guess.
Take at look at item #3 at this URL. This is a pretty straight-forward hack, set use_transactional_fixtures to true and monkeypatch ActiveRecord by pasting that code into your test_helper.rb. This could help in eliminating any intermittent disk IO problems that could be a potential problem.
Another thing you can try is restricting your SQLite test database by setting the filename for that database to :memory: in your database.yml file. This should accomplish the same thing as above - eliminate spurious disk IO that may be causing these intermittent issues.