I'm having weird issues with some of my feature tests using Capybara with poltergeist driver.
The test should perform a simple checkout in my online shop.
They all pass fine on my local MacBook as well as on an Ubuntu vagrant box. However on CI services like Codeship, Wercker or Semaphore they fail with the very same error.
My spec:
require 'rails_helper'
describe 'Checkout' do
let!(:product) { FactoryGirl.create(:product) }
it 'checks out via CreditCard', js: true do
visit products_path
expect(page.body).to have_link('Test Product 1')
click_link('Test Product 1')
#rest of spec ommitted
end
end
The error I get on CI is:
2) Checkout checks out via CreditCard
Failure/Error: click_link('Test Product 1')
Capybara::ElementNotFound:
Unable to find link "Test Product 1"
To me this is super weird, as the first expectation 'expect(page.body).to have_link('Test Product 1')' seems to pass but then it fails on the next step where it should actually click the link it just assured to be present on the page?
I then reconfigured poltergeist driver as follows to gather more debug information.
Snippet of rails_helper.rb:
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, {js_errors: false,
#inspector: true,
phantomjs_logger: Rails.logger,
logger: nil,
phantomjs_options: ['--debug=no', '--load-images=no', '--ignore-ssl-errors=yes', '--ssl-protocol=TLSv1'],
debug: true
})
end
Capybara.server_port = 3003
Capybara.app_host = 'http://application-test.lvh.me:3003' # lvh.me always resolves to 127.0.0.1
Capybara.javascript_driver = :poltergeist
Capybara.current_driver = :poltergeist
Capybara.default_wait_time = 5
Now I can see on CI console that the test successfully visits my products_path and the expected html page (including my the link it should click) is being returned.
I removed the rest of the HTML response to make it more readable:
{"name"=>"visit", "args"=>["http://application-test.lvh.me:3003/products"]}
{"response"=>{"status"=>"success"}}
{"name"=>"body", "args"=>[]}
{"response"=>"--- snip --- <div class=\"info\">\n<a class=\"name color-pomegranate\" href=\"/en/products/6\">\nTest Product 1\n</a>\n850,00 \n</div> --- snap ---"}
{"name"=>"find", "args"=>[:xpath, ".//a[./#href][(((./#id = 'Test Product 1' or normalize-space(string(.)) = 'Test Product 1') or ./#title = 'Test Product 1') or .//img[./#alt = 'Test Product 1'])]"]}
{"response"=>{"page_id"=>4, "ids"=>[0]}}
{"name"=>"visible", "args"=>[4, 0]}
{"response"=>false}
{"name"=>"find", "args"=>[:xpath, ".//a[./#href][(((./#id = 'Test Product 1' or contains(normalize-space(string(.)), 'Test Product 1')) or contains(./#title, 'Test Product 1')) or .//img[contains(./#alt, 'Test Product 1')])]"]}
{"response"=>{"page_id"=>4, "ids"=>[1]}}
{"name"=>"visible", "args"=>[4, 1]}
{"response"=>false}
The last two find actions repeat until Capybara reaches its timeout, then the test fails.
I double checked the xpath Capybara uses via some online xpath validators, but as expected it matches the HTML link.
I also used capybara-screenshot gem to dump the HTML body on failure and the link in question is also present.
So why is the test still failing?
Is there any race condition that I'm not aware of? Why is it passing locally but on none of the CI services?
Here are my gem version:
capybara (2.4.4)
capybara-screenshot (1.0.3)
database_cleaner (1.3.0)
factory_girl (4.5.0)
factory_girl_rails (4.5.0)
poltergeist (1.5.1)
rails (4.1.8)
rspec (3.1.0)
rspec-rails (3.1.0)
and phantomjs 1.9.7
While I can't reproduce this, I remember having this problem before. I believe your line:
expect(page.body).to have_link('Test Product 1')
is passing because the link is literally on the body of the html page, even though it may be hidden due to CSS or JS behavior. However, the line:
click_link('Test Product 1')
definitely checks for visibility before clicking the link. You should check your spec_helper.rb configurations to make sure:
Capybara.ignore_hidden_elements = true
is present, so that the first line wouldn't pass. I think I also had to change the first line I mentioned to:
# Change page.body to page, to look at the rendered page, not the literal one
expect(page).to have_link('Test Product 1')
Once you do this, the first line blocks the thread and waits until the link becomes visible. Then the rest of the test will pass.
Hope this solves it.
Related
when i create a feature test for my application get the following error:
ActionController::RoutingError:
No route matches [GET] "/example"
My application uses subdomains and sub-applications(engines/modules) within these subdomains. Now when i set for Capybara the app_host or default_host through an feature_subdomain_helper like
Capybara.app_host = "example.lvh.me" or
Capybara.default_host = "example.lvh.me"
and into my rails_helper.rb i add the following code line
config.extend SubdomainHelpers, type: :feature
I get the same error. Now i think the configured subdomain are not considered by my feature test.
My Rspec Version is: 3.2
and Capybara Version is: 2.4.4
My sample feature test looks like:
require 'rails_helper'
feature 'Example Page' do
scenario 'visit example page' do
visit "/example"
expect(page).to have_content 'Example'
end
end
Have someone an idea what i do wrong?
Edit:
Mainapp routes:
constraints(Subdomain) do
mount Example::Engine => '/', as: 'example'
end
Engine routes:
Example::Engine.routes.draw do
scope '/example', nav_scope: 'example' do
end
end
The names of Capybara.default_host and Capybara.app_host are slightly misleading since they both need to be set as URLs to function properly
Capybara.default_host = "http://example.lvh.me"
If that doesn't fix your issue check rake routes and make sure the action you think is mounted at '/example' really is.
I am writing a Rails app applying BDD using RSpec & Capybara. One of my tests continues to fail. The goal of the test is to check whether for each Machine record displayed on the index page, clicking on the edit link, results in visualising the details edit page. When I run my application, this functionality works. So I guess, there's something wrong with my RSpec scenario.
Here's the failing test:
Failures:
1) existing machines have a link to an edit form
Failure/Error: expect(page).to have_content(#existing_machine.model)
expected to find text "RX22" in "Toggle navigation uXbridge Catalogue Settings Brands Machine Types Machine Groups Repair States Titles User Signed in as john#example.com Sign out Machine details Brand TORO Model Machine type ZITMAAIER Description Engine Purchase Price Unit Price VAT Minimal Stock Current Stock Warehouse Location"
# ./spec/features/machine_spec.rb:50:in `block (2 levels) in <top (required)>'
Here's the code of the test:
RSpec.feature 'existing machines' do
before do
#john = User.create!(email: 'john#example.com', password: 'password')
login_as #john
brand = Brand.create!(name: 'TORO')
machinegroup = Machinegroup.create!(name: 'GAZON')
machinetype = Machinetype.create!(name: 'ZITMAAIER', machinegroup_id: machinegroup.id)
#existing_machine = Machine.create!(brand_id: brand.id, model: 'RX22', machinetype_id: machinetype.id, description: 'fantastic machine', engine: '100PK' )
end
scenario 'have a link to an edit form' do
visit '/machines'
find("a[href='/machines/#{#existing_machine.id}/edit']").click
expect(page).to have_content('Machine details')
expect(page).to have_content(#existing_machine.model)
end
end
When debugging the scenario, the #existing_machine object seems correctly populated through the .create!() method in the before do block.
screenshot of debug window in IDE
When inspecting the page.html in the debugger, I do see the "RX22" string appearing.
screenshot of page.html inspection
So why does RSpec/Capybara not see the same content when executing the expect(page).to have_content(#existing_machine.model)?
RX22 is the value of an input element not text content so you need to check for it differently. Something like
expect(page).to have_field('Model', with: 'RX22')
should work
Thanks in advance!
Sidekiq is working just fine, but I cannot manage to test it with Devise Async, or should I say that I cannot test the latter?
According to Sidekiq's documentation, when test mode is set to fake!, any job given to a worker is pushed to an array named jobs of that same worker. So it is trivial to test the increase of this array.
But, with Devise Async, it is not so trivial, although its backend includes Sidekiq::Worker. Here's a small list of things that I tried to test:
Devise::Async::Backend::Sidekiq.jobs
Devise::Mailer.deliveries
ActionMailer::Base.deliveries
Devise::Async::Backend::Worker.jobs
None of these testing subjects points an increase in size. Since Devise sends its emails as models callbacks, I tried testing both in a model and in a controller spec. Using Factory Girl and Database Cleaner, I also tried both modes: transaction and truncation. Needless to say that I also tried both modes of Sidekiq: fake! and inline!.
What am I missing?
As mentioned in the documentation, you can check the queue size as
Sidekiq::Extensions::DelayedMailer.jobs.size
Was working on this issue, chanced upon a beautiful implementation done by gitlab which I thought might be helpful in testing devise-async or email that are push via the sidekiq queue.
spec_helper.rb
email_helpers.rb
By adding these lines in spec_helper.rb
# An inline mode that runs the job immediately instead of enqueuing it
require 'sidekiq/testing/inline'
# 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 EmailHelpers
# other configurations line
end
And adding /spec/support/email_helpers.rb
module EmailHelpers
def sent_to_user?(user)
ActionMailer::Base.deliveries.map(&:to).flatten.count(user.email) == 1
end
def should_email(user)
expect(sent_to_user?(user)).to be_truthy
end
def should_not_email(user)
expect(sent_to_user?(user)).to be_falsey
end
end
To run the test for example testing your forgot password, I am assuming you know rspec, factorygirl, capybara
/spec/features/password_reset_spec.rb
require 'rails_helper'
feature 'Password reset', js: true do
describe 'sending' do
it 'reset instructions' do
#FactoryGirl create
user = create(:user)
forgot_password(user)
expect(current_path).to eq(root_path)
expect(page).to have_content('You will receive an email in a few minutes')
should_email(user)
end
end
def forgot_password(user)
visit '/user/login'
click_on 'Forgot password?'
fill_in 'user[email]', with: user.email
click_on 'Reset my password'
user.reload
end
end
You would notice that in this test implementation
will cause sidekiq to run the job instead of enqueuing it,
The user model email attribute must be called email or you can just replace the code above.
ActionMailer::Base.deliveries.map(&:to).flatten.count(user.email) == 1 check to see ActionMailer::Base.deliveries is delivering to user.email
I am trying to use Minitest in a fresh Rails 4 install. My understanding is that if I have a class that doesn't inherit from ActiveRecord then I should be able to use Minitest itself, without Rails integration:
#test/models/blog.rb
require "minitest/autorun"
class Blog < Minitest::Unit::TestCase
def setup
#b = Blog.new
end
def test_entries
assert_empty "message", #b.entries
end
#app/models/blog.rb
class Blog
attr_reader :entries
def initialize
#entries = []
end
I run the test with ruby test/models/blog.rb.
My problem comes with the setup method. If I don't include an entry for my blog, the tests fails with the message that there are the wrong number of arguments in setup. If I include an entry in my setup message #b = Blog.new entries: "Twilight", my test fails in the test_entries method because entries is an undefined method.
You have a couple problems. First, you are not requiring "test_helper", which means that rails isn't getting loaded when you run this test, which means that the mechanism rails uses to resolve missing constants isn't loaded. You will either need to require the helper or require the blog file directly. Second, you are overwriting the constant you want to test with the test, which is why you are getting confusing messages. Name the test class BlogTest instead to avoid this.
This is what I think you are trying to do:
require "minitest/autorun"
require "models/blog" # Assuming "app" is in your load path when running the test
#require "test_helper" # Or require this instead if you need to use DB
class BlogTest < Minitest::Unit::TestCase
def setup
#b = Blog.new
end
def test_entries
assert_empty #b.entries, "Blog entries should be empty"
end
end
Trying to make a simple application in rails 3.
If I create a team model with rails g scaffold team name:string && rake db:migrate, then run rake, I get success from the prebuilt tests.
If I simply add validates_uniqueness_of :name to the team model. The functional tests fail with
1) Failure:
test_should_create_team(TeamsControllerTest) [/test/functional/teams_controller_test.rb:20]:
"Team.count" didn't change by 1.
<3> expected but was
<2>.
I modified tests/fixtures/teams.yml to look like this:
one:
name: MyString
two:
name: MyString2
The test still fails.
It can't get much more basic than this; what have I missed?
Fixtures basically represent model instances that are in the database.
If you look at the top of test/functional/teams_controller_test.rb you'll see
setup do
#team = teams(:one)
end
and then in your failing functional test you'll have
post :create, :team => #team.attributes
This is what's happening: you're trying to create a new team with the same attributes as "the team fixture named :one". Since both would have the same name (since they have the exact same attributes), the uniqueness validation is failing.
Try replacing your setup block with this
setup do
#team = teams(:one)
#team.name = 'unique name'
end
Now you'll be creating a new team with the name 'unique name' (which isn't in the database according to the fixtures), and your test will pass.