Rspec is not creating database records accessible from poltergeist - ruby-on-rails

I'm having terrible trouble getting Poltergeist and RSpec to play together nicely.
I've written the following test:
it "allows the trainer to view a runner" do
visit '/'
all(:xpath,'//a[#id="get-started"]').first.click
fill_in :name, with: "New Admin"
fill_in :email, with: "admin#test.org"
fill_in :password, with: "letmein"
fill_in :password_confirmation, with: "letmein"
all(:xpath,'//input[#id="get-started-submit"]').first.click
#runner_1 = FactoryGirl.create(:runner, name: "Axel", email: "axel#test.org")
visit '/runners/axel'
debugger
Effectively, what the above is doing is registering 'New Admin' with the password, 'letmein', then trying to view the runner profile page for 'Axel'.
Where the debugger interrupts, I can see that #runner_1 (Axel) has been created:
Runner.friendly.find('axel')
>> #<Runner id: 2, email: "axel.manzano#hotmail.fr",........>
However, when trying to visit '/runners/axel', Poltergeist reports:
ActiveRecord::RecordNotFound
It's not an issue with routes, or anything like that.
Having explored this bug a little further, in fact, it seems anything created in the test file doesn't actually get set up in the environment that Poltergeist is accessing.
I can't seem to understand why. Any help greatly appreciated.

Chances are, you are using "transactional fixtures" in rspec. This means that each test run in a database transaction, which is rolled back at the end of test, so that each test has a clean database.
Other threads/programs can not see what is going on in the transaction. Poltergeist runs the server in the separate thread, which means that it can not see anything that is written to the database in rspec (although it can be accessed directly from the rspec code).
There is a description of this phenomenon on the capybara homepage. The solution is to disable the transactional feature in rspec-rails and use something like DatabaseCleaner to reset the database after a test.
This will work, but unfortunately truncating or deleting the database contents is somewhat slower than they would with the transactional approach - this is why the tranasactions are the default in the first place.

Related

How to fix a flaky Capybara test for the Devise password reset flow

I have a flaky system test in Rails that I can't seem to fix. Each time this test initially started failing I kept adding a few seconds to capybara's using_wait_time method and the test would pass for a while. Usually this test passes in isolation and then fails when I run my entire test suite. Then I bump up the wait time and it will pass for a while when running the entire test suite until I change something else in my app that for whatever reason breaks this specific test.
At first I thought, ok I just need to wait for the browser, this is a system test, but now this wait time has started to creep up to an absurd level. (Side note: a similar thing is happening to a system test for entering a search term into the search bar and waiting for a response.) Is there a better way to test this or telling Capybara to wait for the browser the only way? I know that I can trust Devise and don't need to unit test Devise's password reset mailer, but I would like to test the user's flow through the password reset process.
Here's the flaky test:
require 'application_system_test_case'
class UserPasswordResetTest < ApplicationSystemTestCase
def setup
ActionMailer::Base.deliveries.clear
#user = users(:george)
end
test "should allow you to request a password reset email" do
visit login_path
click_link "Forgot your password?"
assert_current_path "/users/password/new"
using_wait_time(30) do
fill_in "user[email]", with: #user.email
end
click_button "Request password reset"
using_wait_time(25) do
assert_text "You will receive an email with instructions on how to reset your password in a few minutes."
end
end
end
And here are my test helpers:
test/application_system_test_helper.rb
test/test_helper.rb
What is a better way to test this password reset flow that isn't as flaky?
If the test is passing with the extended waits, then you need to look at your test log to see why your controller actions are taking so long to render (Capybara is just waiting for that stuff to appear in the browser, and continues as soon as it does). If the tests aren't reliably passing with extended waits then you'll need to provide details on exactly what errors are being returned.
Also note that when you're only adjusting the maximum wait time for a single call you don't need to use the using_wait_time method, you can just pass wait to the method you're calling
fill_in "user[email]", with: #user.email, wait: 30

Devise login acceptance test with capybara

There are already a few questions on stackoverflow with possible solutions, but none of them seemed to solve my problem, thus I'm asking another one, in hope it's specific enough to not be closed as duplicate.
I am trying to test a customized login form that uses devise. The environment consists of
rails 6.0
rspec-rails 3.9
capybara 3.31
selenium-webdriver 3.142.7
database_cleaner 1.8
The following spec is failing due to a 401 Unauthorized response, rendering the login form again with an error message that the credentials were wrong.
require 'rails_helper'
feature 'Logging in' do
background do
FactoryBot.create :user, email: 'john.doe#example.com',
first_name: 'John',
password: 'password',
password_confirmation: 'password'
end
scenario 'with correct credentials' do
visit new_user_session_path
within('#user-sessions--new') do
find('#user_email').fill_in with: 'john.doe#example.com'
find('#user_password').fill_in with: 'password'
end
find('input[name="commit"]').click
expect(page).to have_content 'John'
end
end
Checking the test.log after temporarily disabling the parameter filters for logging, I can see that the password has been submitted properly, and the user is being created before the login attempt happens, yet the authentication fails. Entering an interactive debugging session and trying to log in manually inside the capybara's spun up browser, the login also fails. The email/password login works in development mode when creating the same user through the console, though. Right now, no other devise features that could impact the login behavior (like confirmable) are used.
Most of the questions I found so far advise the following points:
Disable transactional fixtures: My rails_helper.rb already contains this;
RSpec.configure do |config|
config.use_transactional_fixtures = false
end
Configure Database Cleaner: This was suggested in Failing to test Devise with Capybara and was also already the case before I started to implement capybara specs;
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(
:truncation,
except: %w[ar_internal_metadata schema_migrations]
)
end
config.before :each do
DatabaseCleaner.start
end
config.after :each do
DatabaseCleaner.clean
end
end
Right now I have no other Idea why this scenario could be failing except for maybe the environment in which the tests are run hot having a proper database connection. If someone has an idea that helps, I'd be happy to hear it.
When using Rails > 5.0 it safely shares the database connection between the tests and the app under test Capybara starts. Because of that you don't need database cleaner, and you should be using transactional tests for the speed and isolation benefits. Start by removing all references to database cleaner from your project and re-enabling transactional fixtures (the default). If that doesn't solve your issue then there's a couple of other potential issues.
Your factory isn't creating a valid user - Use the FactoryBot.lint functionality to verify all your factories produce valid objects before your tests - https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#linting-factories
You have a JS error on your page that is causing issues. In development mode each JS asset is served separately which means an error in one doesn't affect the others. In test mode, however, the assets are all concatenated which means an error in one can cause JS in other assets not to be run. Check your browser console for any JS errors and fix them.
If none of that helps please add relevant portions of your test.log to your question
Note: You're writing your tests more verbosely than they need to be. If locating fields to fill_in by id you can just pass the id to fill_in and not need to use separate find calls for every element, you should also use the more semantic click_button when the element you're attempting to click qualifies as a button
within('#user-sessions--new') do
fill_in 'user_email', with: 'john.doe#example.com'
fill_in 'user_password', with: 'password'
end
click_button('commit')
expect(page).to have_content 'John'

Unable to locate element with cucumber step_definition, but able to locate it with capybara stand-alone

I am testing a website that has both a vertical login form on the right, and a horizontal form on the bottom. Both have identically named "email" and "password" fields. So I am employing capybara's within to scope for the fields I'm interested in. The interesting bits of the web form look like this:
I have two separate projects, in which I am experimenting with sauce labs for automation. The first project is the capybara-only example, modified to test the page shown above. The second project is a cucumber implementation of the exact same tests. These are very simple, one-time hard-coded tests, just to get a proof of concept of the two techniques.
Here is the interesting bit of the capybara-only example:
within(:css, ".right-container.login-form") do
fill_in 'email', :with => "greg.gauthier+#{generate_email_suffix}#website.com"
fill_in 'password', :with => 'l33tP#$$w0rd'
click_button 'Submit'
end
Here is the interesting bit of the cucumber step_definition:
When(/^The user enters his information$/) do
within(:css, ".right-container.login-form") do #the page has duplicate forms
fill_in 'email', :with => "greg.gauthier+#{generate_email_suffix}#website.com"
fill_in 'password', :with => 'l33tP#$$w0rd'
click_button 'Submit'
end
end
When I run the capybara-only version, everything works great. The form gets filled in, and the email confirmation gets sent.
However, when I run the cucumber version, I get this error:
Unable to find css ".right-container.login-form" (Capybara::ElementNotFound)
How can this be? It's the exact same page, the exact same capybara method (within, using the :css selector), and the exact same test code. What am I not getting (aside from the fact that I'm probably cuking it wrong)?
Oh, here's what the require list looks like in the sauce_helper:
Capybara-only version:
require "sauce"
require "sauce/capybara"
require 'capybara/rails'
require 'capybara/rspec'
Cucumber version:
require "sauce"
require "sauce/capybara"
require "sauce/cucumber"
Do I maybe need to include the extra capybara gems in the cucumber version?
Ok, I'm embarrassed to admit this, but the reason for this question was ignorance of webdriver behavior, at least as it works with Capybara/Cucumber.
The test in question is the second scenario in a set of scenarios. Apparently, selenium resets the browser to blank, between each scenario. So, test one works perfectly, but test two, three, and so forth fail on ElementNotFound because, of course... the page in question was not even loaded.
I added a Before hook to a hooks.rb file in my support path, and now it's working.
Apologies for cluttering the question stream...
Try adding require 'capybara' prior to require 'sauce' in your "Cumcumber Version"
From sauce GitHub
## In your test or spec helper
require "capybara"
require "sauce/capybara"
Reasoning within calls find. Sauce checks to see if a find method is defined an if so then it creates it's own alias called base_find if not it uses it's own find method which is not the same as capybara's. I think they may have something to do with your issue since it is using sauce's find method.
Not positive this is the answer but you can always give it a shot.

Why are emails not getting in the ActionMailer::Base.deliveries table?

Currently in my development environment I am not seeing anything in the ActionMailer::Base.deliveries table after mail is used. Is there a setting I am missing?
config/environments/development.rb: config.action_mailer.delivery_method = :test.
app/mailers/notifier.rb
def send_email(user)
mail(:to => user.email, :subject => "Welcome to our website!")
end
These are the tests I'm running:
When /^I signup$/ do
visit signup_path
#user = FactoryGirl.build(:user)
fill_in "user_email", :with => #user.email
fill_in "user_password", :with => #user.password
click_button "Join now"
end
Then /^I should receive a confirmation email$/ do
email = ActionMailer::Base.deliveries.last
email.subject.should == "Welcome to our website!"
end
Right now I get the following error for the I should receive a confirmation email step:
undefined method subject for nil:NilClass (NoMethodError)
Thanks!
Another possibility might be that in config/environments/test.rb you have
config.action_mailer.delivery_method = :test
but you have set
config.action_mailer.perform_deliveries = false
so changing to
config.action_mailer.perform_deliveries = true
made it work again in my case.
Old question, I know, but a couple things come to mind:
You talk about the configuration of your development environment (config/environments/development.rb), but it's your test that isn't working. Check config/environments/test.rb and maybe config\application.rb.
I actually wanted to see the email array in my development environment, but it always returned nil in the console. I finally realized that because the array is just a memory object, not in the database, the console (rails c) can't see what's going on in the server (rails s). When I added <%= ActionMailer::Base.deliveries.count %> to a view, I could immediately see the array getting populated in development.
Another "I can't see that process from the console" situation arises if you are using a separate thread or task for sending emails. With delayed_job, I found this answer suggesting you can look at the job (not the actual email) using Delayed::Job.last.handler, which does look at the database so works across processes. However this only works while the job is still in the database, i.e. before a worker has processed it.
The error you are getting says that email is nil. In other words, the deliveries.last method is returning nil because there are no emails in it.
There could be a number of reasons... I'm assuming that you are using cucumber as the testing mechanism?
1. Verify that 'click_button' submits
You could try to put puts or log statements. Make sure that when you run the cucumber feature, it actually prints. Sometimes these things don't work because the front-end logic doesn't work and the test is failing correctly. In other words, the controller endpoint you are expecting to be triggered isn't being triggered.
2. Verify that 'send_email' is right - using a unit test
Next, verify that you are actually sending email. Write a unit test to verify this works. This should be quick and easy to get going. Plenty of articles out there.
Lastly, not really related, but you might want to roll with email-spec which should provide some nice testing methods so you don't have to reinvent the wheel.
After making the changes answered by #Dominik Steiner, if it still didn't work ,
another possibility might be that in config/initializers/mailer.rb, you should have :
ActionMailer::Base.delivery_method = :test
Above was missing in my case and it worked now.

ActiveRecord Objects created in Cucumber not showing in Rails web app

I am using cucumber (cucumber (1.1.0) cucumber-rails (0.3.2)) trying to test the web UI on a rails 2.3.14 application. The problem is that I am creating the user in the feature step:
visit("/")
#admin_user = Factory.create :admin_user
#admin_user.email
#admin_user.update_attributes :password => "testtest", :password_confirmation => "testtest"
#admin_user.register!
#admin_user.activate!
click_link("Login")
fill_in "Email:", :with => #admin_user.email
fill_in "Password:", :with => "testtest"
find("input[type=image]").click
But, when I visit the site and try to login the user is not visible in the database. Calling new_record? on #admin_user returns false but the rails application cannot see it. Any ideas on why this record is not showing up? I have confirmed that both cucumber and the rails application are point to the same database.
As #Ernest said, there are two connections. But turning off transations would slow down your suite, so use this code: https://gist.github.com/470808, and read: http://blog.plataformatec.com.br/2011/12/three-tips-to-improve-the-performance-of-your-test-suite/ especially section 3.
It may happen if Cucumber and Rails are using two different connections. For that case, you may need to turn off transactions. More about the topic here: https://github.com/cucumber/cucumber/wiki/Browsers-and-Transactions
Are you sure they point at the same database? Normally cucumber will run in the test env and you visiting the site will either be in development or production environments.
If you have changed config/database.yml to point the test environment to the development or production database, Cucumber normally wraps each scenario in a transaction which rolls back.

Resources