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.
Related
I've recently started testing my app using RSpec. Being the testing noob that i am, i did not install the full suite of tools that people normally use (namely FactoryGirl, also now known as FactoryBot. And DatabaseCleaner).
Anyway here's what happened. After getting my feet wet with RSpec, i started using FactoryBot so that my tests looks less convoluted. Note that :user is a Devise object.
# spec/models/first_test_spec.rb
FactoryBot.create(:user)
#Other Code
So i'm doing stuff with my first_test_spec.rb and i run it and everything is working perfectly fine. I then create my 2nd spec file
# spec/models/second_test_spec.rb
FactoryBot.create(:user)
#Other Code
Now here comes the problem. My tests are failing because FactoryBot.create(:user) is invalid, due to Devise requiring unique emails. Clearly this indicates that the data from my first_test_spec is persisting hence the error.
So i attempt to install DatabaseCleaner and hopefully clear my Test DB after each run. My rails_helper looks like this:
require 'spec_helper'
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 'rspec/rails'
require 'support/factory_bot'
# Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = false
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
config.infer_spec_type_from_file_location!
config.filter_rails_from_backtrace!
end
I think i've got everything set up correctly, so i'm uncertain if the errors are still occurring due to my DatabaseCleaner being set up wrongly.
Anyway, running rspec still throws the same error, where second_test_spec cannot create the user object because the email already exists (a validation that comes with Devise itself).
I then proceed to run the following:
rake db:test:prepare
rspec spec/models/second_test_spec.rb
And it still throws the same error. So right now, i have no idea if my database is being cleanse after running rspec. But i'm quite certain that i have been unable to purge my test database.
So i guess i really have 2 questions/problems:
1) How do you purge your test database? (Googling reveals that rake db:test:prepare is the way)
2) Is my DatabaseCleaner setup correctly? If so, shouldn't it be purging the database?
UPDATE:
As suggested to me in the comments, using sequence for creating unique fields with FactoryBot is recommended. Obviously that made the problem go away because there would no longer be validation errors from Devise.
I then went on to test a couple of things.
rails c test
I ran this to check my test database and it was indeed empty. This indicates that DatabaseCleaner is working perfectly fine. What i fail to understand then, is why the need to sequence my email creation in FactoryBot? Or i suppose, i fail to understand how does RSpec "compile & run".
puts #user.email
So i wanted to print out the emails to look at the sequencing to see if i'm able to decipher the problem. Here's what happens:
running rspec spec/models/first_test_spec.rb
Tutor email yields the number 2.
running rspec spec/models/second_test_spec.rb
Tutor email yields the number 3.
running rspec
Tutor email yields the numbers 2 & 5.
So i'm not sure if there are "problems" with my test suite. Clearly my original problem has been fixed, and this is a separate topic altogether. But i figured if anyone would like to explain this mystery to anyone else who chances upon this thread may wish to do so.
Seeing your full spec files would help. If you are running the specs as you've written - creating a user outside of an example group or a before block - that will cause records to be written outside of the cleaning strategy scope and can cause data to remain across examples. Your DBcleaner config looks to be set up fine otherwise.
rake db:test:prepare is the correct way to clean out your test db but shouldn't need to be ran often unless you have migration changes. You can jump into a rails console within the test environment rails c test and look around to see if there are any records left behind.
As a side note you can flip config.use_transactional_fixtures from false to true and remove DBcleaner altogether from your app. Just make sure there is no residual data in your database before going this route.
I've noticed a couple specs that are failing intermittently depending on the order they're run.
To isolate them I'm looking for a command where I can enter the seed number and see all the specs listed with line numbers in the order determined by the seed number. Is this possible? using --format=documentation did not provide the info needed.
From there I will note the list of tests run BEFORE the intermittent failure each time it occurs and eventually narrow down to my culprit.
RSpec's JSON formatter outputs the filenames and line numbers in the order they were run:
rspec --seed 1 -f json > out.json
To get just the list of filenames with line numbers from the resulting out.json file:
require 'json'
data = JSON.parse(File.read('out.json'))
examples = data['examples'].map do |example|
"#{example['file_path']}:#{example['line_number']}"
end
Now examples will contain an array of file paths like:
["./spec/models/user_spec.rb:19", "spec/models/user_spec.rb:29"]
In my spec/spec_helper.rb file:
I have created a global variable $files_executed and initialized it to an empty set:
require 'set'
$files_executed = Set.new
Then, inside the RSpec.configure block I added:
config.before(:each) do |example|
$files_executed.add example.file_path
end
And this:
config.after(:suite) do
files_filename = 'files_executed.txt'
File.write(files_filename, $files_executed.to_a.join(' '))
end
And then you can use the content of the file files_executed.txt to feed the rspec command again with the order that was used before.
You can use the --bisect option.
RSpec's --order random and --seed options help surface flickering examples that only fail when one or more other examples are executed first. It can be very difficult to isolate the exact combination of examples that triggers the failure. The --bisect flag helps solve that problem.
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.
In my Rails application, I have a set of cucumber acceptance tests that test various pages of my application. With cucumber, tagging a specific test (scenario) with #javascript causes that scenario to run using a JavaScript driver instead of a simpler driver that does not support JavaScript.
Is there an easy way for my tests to determine whether they are currently being run with a driver that supports JavaScript or one that doesn't? I want this so that I can make my tests behave slightly differently if they are being run with JavaScript enabled.
In case anyone's interested, I took a look at the documentation for Capybara and found another possible solution:
if Capybara.current_driver == Capybara.javascript_driver
# Supports JavaScript
else
# Doesn't support JavaScript
end
It's kind of obnoxious, but this is what I landed on. Think I got it from a gist somewhere, sorry I lost the link to it but at least it's pretty straightforward:
def javascript_test?
[:selenium, :webkit, :chrome, :poltergeist].include?(Capybara.current_driver)
end
If you define (or require in your Gemfile) other js enabled drivers, you'd have to add them to this list.
Looks to me from this answer like the easiest way to do this is to set an instance variable in a Before hook:
Before('#javascript') do
#javascript = true
end
Then you can test if a scenario is tagged in step definitions by checking the value of that instance variable:
When /^I go to the homepage$/ do
if #javascript
...
else
...
end
end
I've tested this and it seems to work.
You could also simply probe for the capability and rescue any exception. This is probably the most precise and compatible approach:
def support_js?
return #support_js unless #support.nil?
#support_js = begin
page.evaluate_script("1 + 1")
true
rescue Capybara::NotSupportedByDriverError => _err
false
end
end
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.