How to test devise mail delivery with devise, rspec, capybara, and mailspec - ruby-on-rails

I'm trying to test if devise is sending out confirmation emails. This is becomming to be a bit of a challenge to me because, the tests use a different environment in Rails and I'm not quite sure if I'm going the right route.
Here's my rspec test.
describe "Sign up:", :type => :feature do
before(:each) do
#user = FactoryGirl.build(:user)
visit root_path
click_link "Sign Up"
fill_in "Name", :with => #user.name
fill_in "Email", :with => #user.email
fill_in "Password", :with => #user.password
fill_in "Password confirmation", :with => #user.password
click_button "Sign up"
end
describe "user gets a confirmation email" do
subject { ActionMailer::Base.deliveries.last }
it { is_expected.to deliver_to(#user.email) }
end
This is the message when I run the spec.
Failure/Error: it { is_expected.to deliver_to(#user.email) }
NoMethodError:
undefined method `perform_deliveries' for nil:NilClass
I would like to test this feature, but can Rails send out emails in a test environment? If so what kind of code makes this type of code pass? I have the mailer set up to sendgrid, so I can show that as well. User's of Devise are set to confirmable, so a confirmation email is supposed to be coming out.

Make sure you have this set in your you have this set in config/environments/test.rb:
config.action_mailer.delivery_method = :test
In your spec, you can also just test that ActionMailer::Base.deliveries has changed:
describe "Sign up:", :type => :feature do
# ...
it "sends a confirmation email" do
expect {
click_button "Sign up"
# for example. the point is to wait for the AJAX response
page.find('p.thanks', text: 'Thanks for signing up')
}.to change { ActionMailer::Base.deliveries.size }.by(1)
end
end
Similarly, if you're using Sidekiq, you can instead check that the number of Sidekiq jobs went up.
Personally, I'd save testing the specifics of the email for a unit test, and use the feature test to make sure that everything's wired together.
Also, if you want to verify manually that the emails are getting sent, Mailcatcher is pretty nice.

You could try to write in test environment:
config.action_mailer.perform_deliveries = true
I hope it will help You!

Related

Testing Devise Using capybara rspec and factory girl

i'm new at rails and i'm testing my devise gem User with capybara rspec and factory girl.
Here's spec code:
require 'rails_helper'
RSpec.describe User, type: :request do
it "displays the user's email after successful login" do
user = FactoryGirl.create(:user, email: 'test#test.com', password: 'password')
visit root_url
fill_in 'Email', with: 'test#test.com'
fill_in 'Password', with: 'password'
click_button 'Log in'
expect page.has_content?('jdoe')
end
end
The problem is in
expect page.has_content?('jdoe')
No matter what i put instead 'jdoe' test works perfectly without any errors.
Here's factory:
FactoryGirl.define do
factory :user do
email 'test#test.com'
password 'password'
password_confirmation 'password'
end
end
Maybe I missed something or going with a wrong way. How should I test here?
# spec/features/sessions_spec.rb
require 'rails_helper'
RSpec.feature "Sessions" do
scenario "displays the user's email after successful login" do
user = FactoryGirl.create(:user)
visit root_url
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
click_button 'Log in'
expect(page).to have_content("Signed in successfully")
# will give a false postitive if form is rerended?
expect(page).to have_content(user.email)
end
end
A few things here:
Use a feature and not a request spec for end to end testing.
Don't hardcode object attributes. The whole point of factories is that the factory takes care of generating unique data so that the tests don't give false positives due to residual state.
Use expect(page).to instead of expect page.should. expect(page).to sets a subject and uses a RSpec matcher which will show you the text of the page in an error message if it does not match.

Within a feature spec, how to test that a Devise mailer is called successfully?

I have a feature test for user registration. How do I test that Devise confirmation instructions are sent correctly? I don't need to test the content of the email, only that the mailer has been called.
I am sending mails in the background.
#user.rb
def send_devise_notification(notification, *args)
devise_mailer.send(notification, self, *args).deliver_later
end
I have tried a few approaches that work for other mailers, including
it "sends the confirmation email" do
expect(Devise.mailer.deliveries.count).to eq 1
end
and
it "sends the confirmation email" do
message_delivery = instance_double(ActionMailer::MessageDelivery)
expect(Devise::Mailer).to receive(:confirmation_instructions).and_return(message_delivery)
expect(message_delivery).to receive(:deliver_later)
end
none of which are working as expected for Devise messages.
What am I doing wrong?
Edit
The feature spec looks like this:
feature "User signs up" do
before :each do
visit '/'
click_link 'Sign up'
fill_in 'user_email', with: valid_attributes[:email]
fill_in 'user_password', with: valid_attributes[:password]
fill_in 'user_password_confirmation', with: valid_attributes[:password]
click_button 'Sign up'
end
it "sends the confirmation email" ...
end
Since you're doing a high level feature spec, I would wager that as a result of clicking the 'Sign up' button, what you want to confirm is that an email job has been added to the queue.
In order to do that, you may have to slightly change your spec set up:
feature "User signs up" do
before :each do
visit '/'
click_link 'Sign up'
fill_in 'user_email', with: valid_attributes[:email]
fill_in 'user_password', with: valid_attributes[:password]
fill_in 'user_password_confirmation', with: valid_attributes[:password]
end
it "queues up a confirmation email job" do
expect { click_button 'Sign up' }.to \
have_enqueued_job(ActionMailer::DeliveryJob)
end
end
You can have a look at the have_enqueued_job matcher for more options if the above one doesn't quite suit your use case.

Trouble with refactoring code for rspec feature tests in rails 4

I am trying to give the user of my web app the ability to login with a password. I am rolling my own authentication instead of using a gem. I read this article about refactoring Rspec/Capybara tests:
http://robots.thoughtbot.com/rspec-integration-tests-with-capybara
I liked what I read and decided to give refactoring a try. I created a session helper file for my feature tests.
module Features
module SessionHelpers
def sign_in
user = create(:user)
visit '/authentications/new'
fill_in 'Login', with: user.name
fill_in 'Password', with: user.password
click_button 'Sign in'
end
end
end
I then called the sign_in function in my login tests. Here is a little sample.
require 'spec_helper'
feature "signing in" do
before :each do
User.create(:name => 'user#example.com', :password => 'caplin')
end
scenario "user who logs in with correct credentials" do
sign_in
expect(page).to have_content 'Hi user#example.com'
end
end
Unfortunately, I keep getting this error message:
2) signing in user who logs in with correct credentials
Failure/Error: sign_in
NoMethodError:
undefined method `create' for #<RSpec::Core::ExampleGroup::Nested_3:0x007ffc85012438>
# ./spec/support/features/session_helpers.rb:4:in `sign_in'
# ./spec/features/user_logs_in_spec.rb:13:in `block (2 levels) in <top (required)>'
Basically, I need some way to grab the user I created and pass it into the sign_in function. Any hints?
I'm guessing your first issue is a different test configuration than the one the ThoughBot example has. create is not to my knowledge a default method available in RSpec; I'm going to guess they've added every FactoryGirl method to the testing scope. If you're using FactoryGirl, you can get the same behavior by just namespacing the create command:
def sign_in
user = FactoryGirl.create(:user)
visit '/authentications/new'
fill_in 'Login', with: user.name
fill_in 'Password', with: user.password
click_button 'Sign in'
end
However, this won't quite get you everything that you asked for, since you still won't be able to add a custom user. An easy way for this would allow for a user to be passed in:
def sign_in(user=nil)
user ||= FactoryGirl.create(:user)
...
end
This will create the user for you if you don't pass one in on the sign_in call.
Going back to the spec you posted, you'd want to change it to this:
feature "signing in" do
before :each do
#user = User.create(:name => 'user#example.com', :password => 'caplin')
end
scenario "user who logs in with correct credentials" do
sign_in(#user)
expect(page).to have_content 'Hi user#example.com'
end
end
You'd need to attach the user you created to a variable (#user), then pass it to the sign_in as needed.
Problem in you model
module Features
module SessionHelpers
def sign_in
user = create(:user) # <- this method allow only in FactoryGirl
visit '/authentications/new'
fill_in 'Login', with: user.name
fill_in 'Password', with: user.password
click_button 'Sign in'
end
end
end
i use another way. Create a class and include FactroyGirl methods and Capybara::DSL like this
class Features
include FactoryGirl::Syntax::Methods
include Capybara::DSL
def sign_in
user = create(:user) #<- FactroyGirl
visit '/authentications/new' #<- Capybara
fill_in 'Login', with: user.name #<- Capybara
fill_in 'Password', with: user.password #<- Capybara
click_button 'Sign in' #<- Capybara
self #<- return page
end
end
in spec
feature "signing in" do
let(:login_user) { Features.new }
scenario "user who logs in with correct credentials" do
page = login_user.sign_in
expect(page).to have_content 'Hi user#example.com'
end
end
You can accomplish this by including FactoryGirl in your tests. Your RSpec configuration block (in spec_helper.rb or in the new version of RSpec rails_helper.rb) should look like this:
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
end

integration tests for devise password recovery using rspec, capybara and email_spec

I'm trying to test the password recovery using rspec/capybara using email_spec
My test.rb contains:
config.action_mailer.delivery_method = :test
My test goes like this:
feature User do
let!(:user){ FactoryGirl.build(:user) }
before(:each) do
visit root_path
click_link "Sign up/in"
end
scenario "recover password" do
user.save!
click_link "Forgot password?"
fill_in "Email", :with => user.email
click_button "Send me reset password instructions"
unread_emails_for(user.email).should be_present
end
My test fails like so:
1) User recover password
Failure/Error: click_button "Send me reset password instructions"
ActionView::Template::Error:
Missing host to link to! Please provide the :host parameter,
set default_url_options[:host], or set :only_path to true
If I set the default_url_options, rspec tries to send the mail. I'm not too sure about :only_path.
Can anyone please point me in the right direction? Many thanks.
Set the following line in the /config/environments/test.rb:
config.action_mailer.default_url_options = { :host => 'localhost:3000' }

Trying to test sending of email in rSpec but ActionMailer::Base.deliveries.empty? is true

I have no idea why it's not working. After hours of trying to figure this out I wrote a small test to check if the ActionMailer::Base.deliveries was empty and it was.
When I test my reset form it works and mail is sent but when I run test it doesn't seem to store anything in the array.
require 'spec_helper'
describe "Passwords" do
describe "reset password" do
it "emails user when requesting password reset" do
visit login_path
click_link "Forgot Password?"
response.should render_template "passwords/new"
response.should have_selector :h1, :content => "Reset Password"
fill_in "password_reset[email]", :with => "foobar#gmail.com"
click_button "Reset Password"
response.should render_template "users/new"
response.should have_selector :div, :id => "flash_notice", :content => "Email sent with password reset instructions."
ActionMailer::Base.deliveries.empty?.should be_true
# mail = ActionMailer::Base.deliveries.last
end
end
end
Just found out a great gem to test emails with rspec: https://github.com/bmabey/email-spec
Hope it will help.
I always seem to do silly things like this:
Because I created the passwords_spec file manually and not using the rspec generator I forgot to add "require 'spec_helper'" at the top of the file. Something that the generator would have done automatically.
The last few days I have been figuring out my silly mistakes. Funny how all this happened when I got lazy and decided to build my app first then test after. Well I've learnt from my mistake. Test first!

Resources