Sporadically succeeding cucumber tests - ruby-on-rails

I have a cucumber step that involves the following:
When /^I select from the Open table project with details "([^"]*)"$/ do |details|
find('.details', :text => details).click
end
It fails with the following error message Unable to find css ".details" (Capybara::ElementNotFound)
This step fails about 90% of the time I run it, but occasionally it works. I inspect the page source, and the page does indeed have a css class .details with text that corresponds to details.
Is this some kind of timing issue or issue with cucumber? Or is it more likely that, now that Cucumber has driven me bat-poop insane, I'm not seeing things correctly?

Based on this post: Why does it seem like the Capybara "wait for page to load" timer works for matchers but not finders?
It seems that the find method does not wait for the page to be loaded before doing it's thing. Which means you have a race condition, and cucumber is losing about 90% of the time.
You can warp it in a wait_until block to specify your own timeout and pass the test
page.wait_until(5) { find('.details', :text => details) }
find('.details', :text => details).click

Related

Frontend testing with CircleCI and Minitest: Tests don't wait long enough for page to load

So my tests seem to pass about 75% of the time. The other 25% fail because the testing suite isn't waiting long enough for the page to completely load. On our local machines the test takes about 35s, but on CircleCI's logs it's only 5s. (On local I run the tests with BROWSER=chrome m path/to/test.file)
I'm new to this tech stack so any help is greatly appreciated, even if it's just appropriate reference docs.
it 'should use this form' do
assert page.has_content?('#target_form')
within '#target_form' do
# fill in the form and apply payment
end
# it will throw errors here "can't find css..."
# the text/element won't even have loaded yet
# by the time the test is run
assert_equal '24', find_qa('price').text
end
The way you're writing your assertions isn't utilizing Capybara's waiting/retrying behavior so running on slower hardware (CircleCI compared to your local box) can cause your tests to fail. assert_equal evaluates the two parameters compares them, and it's done. This isn't good because Capybara assumes every action could perform asynchronous actions so it doesn't necessarily wait for a button click to submit and load a new page (because it has no way of knowing what action the button click may produce). However if you use the Capybara provided assertions it will wait/retry the comparison up to Capybara.default_max_wait_time seconds for the comparison to be true. I'm not sure how your find_qa method is defined, but if you've declared a custom :qa selector you could do something like
assert_selector :qa, 'price', text: '24'
If find_qa is just doing a CSS selector then you could do
assert_selector :css, "whatever find_qa('price') produces as a css selector", text: '24'
or you could do
find_qa('price').assert_text('24')
Since you're using minitest you probably want to read - https://github.com/teamcapybara/capybara#using-capybara-with-minitest - and configure Capybara's minitest matchers so the counts of assertions run are correct, and to provide a bunch more specific assertions that will utilize Capybara's waiting/retrying behavior. See https://github.com/teamcapybara/capybara/blob/master/lib/capybara/minitest.rb for the matchers added, which will let you write things like
assert_text find_qa('price'), '24'
assert_xpath 'an XPath selector', ...
assert_title ...
assert_current_path ...
etc.

Cucumber/Capybara: Test is not accurately reflecting real world use. It is ignoring page errors and passing tests

I did a total rearrangement of my routes so I'm having to go back and change some path names. I ran a cucumber test for my navigation and it came back a success. Knowing that I have some old routes to change I thought that was an odd result so I did some manual checking to confirm my suspicions.
As an example I clicked on a page that was supposed to have a old and obsolete path <li><%= link_to p.title, forum_post_path(p) %></li> that's breaks the page when I am manually clicking around I get a no method error as I should.
But, when I run the cucumber test and use launchy to save and open page. I don't get that error. It loads like there isn't a problem at all. The launchy page that I'm given loads the link_to helper with the bad _path like there isn't a problem at all...
The only thing I can think of is that the words that it is expecting would be there and perhaps the page is loading correctly for a brief moment before Rails spits out the method error and Cucumber is picking up on the positive result first before it returns an error.
Any possible things I can look at? I would hate to get false positives.
Edit: I just added a 1 second sleep timer (should be more than enough) and cucumber still gives me a pass.
Here are the tests so you can view it:
# navigation.feature
Scenario: As a user I want to be able to view a specific forum and it's posts within.
Given There is a User
Given There is a Forum
Given I am on the index
When I click the "Test forum name" link
Then I should see "Name: Test forum name"
Then I should see "Description: test description with a minimum of 20 characters.."
And...
#navigation_steps.rb
Given(/^There is a User$/) do
User.create!(email: "user#test.com", password: "password#1")
expect(User.first.email).to eq("user#test.com")
end
Given(/^I am on the index$/) do
visit root_path
end
Given(/^There is a Forum$/) do
Forum.create!(name: "Test forum name", description: "test description with a minimum of 20 characters..", user_id: User.first.id)
expect(Forum.last.name).to eq("Test forum name")
end
When(/^I click the "([^"]*)" link$/) do |link|
click_link link
end
Then(/^I should see "([^"]*)"$/) do |message|
expect(page).to have_content(message)
end
Screenshots:
You don't say what exactly your tests are checking for, but a couple of possible reasons for this are:
You're using something like spring to keep your test environment loaded - If you are it may not be seeing changing to routes.rb and may require restarting before it knows the routes have changed. Solution: Restart spring or whatever your are using to keep the test env loaded
You incorrectly have the web-console gem in the test environment (it should only be in the development environment) which catches any errors and produces the nice error page. If that error page has the text on it your test checks for (possible since it includes the surrounding code) then your test can pass - Solution: remove web-console from the test environment in your Gemfile.

Why rspec doesn't wait completion of previous command?

My rails-app contain rspec-tests using Capybara poltergeist (which uses phantomjs). Frontend is Backbone.js. Some tests are sometimes passed, sometimes not. Debugging shows that the right content is not loaded at the time of inspection. If i add an delay, e.c. sleep (inspection_time = 1), then the test passes.
describe 'Direct visit page / # / desk / page / [ID]' do
    before {sign_in user}
    it 'should contain page title' do
      visit "/#/desk/pages/#{page.id}"
      #sleep (inspection_time = 1)
      expect (page) .to have_css (". page-detail p.title",: text => page.title)
    end
  end
js-tests are performed and so long, and yet unreliable delay increases execution time. Is there a good solution for drivers poltergeist (gem 'poltergeist', '1.6.0')?
Might be worth abandon poltergeist in favor of, for example capybara-webkit?
Testing asynchronous requests with capybara can be tricky. As you noticed capybara does not know to wait for a async request to finish. But the good news is that matchers are smart enough to wait for something to appear.
For example if you call
expect(page).to have_css(".new_data")
And a div with new_data class does not initially exist capybara will wait for X seconds (defined in configuration) to see if it appears. However this is sometimes not enough, and you are probably dealing with some kind of a race condition. Thoughtbot wrote a good article on how to resolve this.

Ruby on Rails - RSpec Javascript Test with Capybara (new to RoR)

New to Ruby, Rails and TDD. I'm using RSpec with Capybara and Capybara webkit.
Trying to test if a div element exists on a page.
Test Code:
require 'spec_helper'
describe "Login module" do
before do
visit root_path
end
it "should have a module container with id mLogin" do
page.should have_css('div#mLogin')
end
it "should have a module container with id mLogin", :js => true do
page.evaluate_script('$("div#mLogin").attr("id")').should eq "mLogin"
end
end
The first test passes but the second test fails with:
Login module should have a module container with id mLogin
Failure/Error: page.evaluate_script('$("div#mLogin").attr("id")').should eq "mLogin"
expected: "mLogin"
got: nil
Ran the JS in browser dev tools and get "mLogin" rather than nil.
Any ideas? Thanks.
find('div#mLogin')[:id].should eq 'mLogin'
See this from doc:
#evaluate_script
Evaluate the given JavaScript and return the result. Be careful when using this with scripts that return complex objects, such as jQuery statements. execute_script might be a better alternative.
evaluate_script always return nil, as far as I remember.
Anyway, your second test seems like is testing if capybara works, because your first test is enough.
One likely problem is that the have_css matcher supports Capybara's synchronization feature. If the selector isn't found right away, it will wait and retry until it is found or a timeout elapses.
There's more documentation about this at http://rubydoc.info/github/jnicklas/capybara#Asynchronous_JavaScript__Ajax_and_friends_
On the other hand, evaluate_script runs immediately. Since this is the first thing you do after visiting the page, there's a race condition: it's possible that it executes this script before the page has finished loading.
You can fix this by trying to find an element on the page that won't appear until the page is loaded before you call evaluate_script.
Alternately, you can wrap your call in a call to synchronize to explicitly retry, but this is not generally recommended. For situations like this, you're much better off using Capybara's built-in matchers. The evaluate_script method should only be used as a last resort when there is no built-in way to accomplish what you need to do, and you need to take a lot of care to avoid race conditions.

How to avoid concurrency issues by running #javascript scenarios?

I am using Ruby on Rails 3.2.2, cucumber-rails-1.3.0, rspec-rails-2.8.1 and capybara-1.1.2 with the Selenium driver. I have an issue about concurrency on running JavaScript scenarios (supposed that my case is related to a concurrency issue).
In my .feature file I have:
Feature: ...
...
#javascript
Scenario: ...
Given ...
When I log out
Then I should be redirected to the home page
In my step definition .rb file I have:
Then /^I should be redirected to the home page$/ do
current_path.should == root_path
end
When I run the cucumber command line in the Terminal window so to run tests, I get this error:
$ cucumber
Using the default profile...
Feature: ...
...
#javascript
Scenario: ...
...
When I log out
Then I should be redirected to the home page
expected: "/"
got: "/user" (using ==) (RSpec::Expectations::ExpectationNotMetError)
...
However if in my step definition .rb file I use
Then /^I should be redirected to the home page$/ do
sleep(1)
current_path.should == root_path
end
it works as expected: all tests pass.
So, even if the problem seems to be solved, there is a better way to handle my issue? if so, what is the "right"/"common used" solution (maybe using some Capybara API)?
It isn't concurrency. The problem is that cucumber isn't synchronized with your application. The test side doesn't know anything about whether your action has completed its execution or not. So when you're asserting the path your application hasn't generated the response to actually do redirect.
If you'll take a look into capybara you'll see that it polls for the element to appear. (With guard max wait time). So you can either take the same approach and do a polling for some time or you can take a lazier approach and in your test before testing for path add a step that will check for some content on the target page to appear. So you can rely on capybara's built in mechanism.

Resources