I am new to RSpec and TDD and I am having difficulties writing a RSpec test to test if Devise is actually sending the confirmation email after a user signs up. I know that my application is working as expected because I have physically tested the functionality in both development and production. However, I am still required to write the RSpec test for this functionality and I cannot figure out how to send a confirmation email through RSpec tests.
factories/user.rb
FactoryGirl.define do
factory :user do
name "Jack Sparrow"
email { Faker::Internet.email }
password "helloworld"
password_confirmation "helloworld"
confirmed_at Time.now
end
end
spec/models/user_spec.rb
require 'rails_helper'
RSpec.describe User, type: :model do
describe "user sign up" do
before do
#user = FactoryGirl.create(:user)
end
it "should save a user" do
expect(#user).to be_valid
end
it "should send the user an email" do
expect(ActionMailer::Base.deliveries.count).to eq 1
end
end
end
Why is Devise not sending a confirmation email after I create #user? My test returns ActionMailer::Base.deliveries.count = 0. As I said, I am new to RSpec and TDD so am I completely missing something here?
Devise uses its own mailer, so try Devise.mailer.deliveries instead of ActionMailer::Base.deliveries if putting the test in the right controller's file doesn't work by itself.
Related
Trying to create an Rspec/Factory girl test to make sure that Devise's confirmation on signup is covered - the site has 3 languages (Japanese, English, Chinese) so I want to make sure nothing breaks the signup process.
I have the following factories:
user.rb << Has everything needed for the general user mailer tests
signup.rb which has:
FactoryGirl.define do
factory :signup do
token "fwoefurklj102939"
email "abcd#ek12o9d.com"
end
end
The devise user_mailer method that I want to test is:
def confirmation_instructions(user, token, opts={})
#user = user
set_language_user_only
mail to: #user.email,
charset: (#user.language == User::LANGUAGE_JA ? 'ISO-2022-JP' : 'UTF8')
end
I cannot for the life of me figure out how to get the token part to work in the test - any advice or ideas?
I have been trying something along these lines (to check the email is being sent) without success:
describe UserMailer, type: :mailer do
describe "sending an email" do
after(:all) { ActionMailer::Base.deliveries.clear }
context "Japanese user emails" do
subject(:signup) { create(:signup) }
subject(:user) { create(:user) }
subject(:mail) do
UserMailer.confirmation_instructions(user, token, opts={})
end
it "sends an email successfully" do
expect { mail.deliver }.to change { ActionMailer::Base.deliveries.size }.by(1)
end
end
end
end
The resulting error is undefined local variable or methodtoken'and I cannot work out why it is not coming from thesignup` factory. I tried changing
subject(:mail) do
UserMailer.confirmation_instructions(user, token, opts={})
end
to
subject(:mail) do
UserMailer.confirmation_instructions(user, signup.token, opts={})
end
but then I received this error:
Failure/Error: subject(:signup) { create(:signup) }
NameError:
uninitialized constant Signup
EDIT: I forgot to mention something important - the actual code all works for user signups in all 3 languages, so I am certain that this is definitely my inexperience with testing at fault.
subject(:mail) do
UserMailer.confirmation_instructions(user, user.confirmation_token)
end
This varies of course depending on what your exact implementation is but your user class should be generating the token:
require 'secure_random'
class User
before_create :generate_confirmation_token!
def generate_confirmation_token!
confirmation_token = SecureRandom.urlsafe_base64
end
end
Creating a separate factory is unnecessary and won't work since FactoryGirl will try to create an instance of Signup which I'm guessing that you don't have.
Factories are not fixtures.
I'm writing basic tests for a simple CRUD rails application. I am using devise for authentication and Factory Girl for object creation whilst testing, after testing I am clearing the test.db with the Database Cleaner gem.
I am testing a controller with RSpec but need an admin user to be signed in for this to be a 'true' test.
So far I have been following documentation but I don't believe it is working.
I have a test which checks if the count has been changed by one:
describe "POST create" do
context "with valid attributes" do
it "creates a new room" do
expect{ post :create, room: FactoryGirl.attributes_for(:room) }.to change(Room,:count).by(1)
end
When I run the test suite I get the error:
expected #count to have changed by 1, but was changed by 0
From reading around it seems I need to setup authentication with my tests. To do this I have created relevant Factories:
# Devise User Class
factory :user do
email "basicuser#mvmanor.co.uk"
password "u"
password_confirmation "u"
admin false
customer false
end
# Admin
factory :admin, class: User do
email "basicadmin#mvmanor.co.uk"
password "a"
password_confirmation "a"
admin true
customer false
end
I have also created the relevant Devise mappings macros:
module UserControllerMacros
def login_admin
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:admin]
sign_in FactoryGirl.create(:admin) # Using factory girl as an example
end
end
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
sign_in FactoryGirl.create(:user) # Using factory girl as an example
end
end
end
I'm not sure if I'm heading in the right direction with this. I certainly need my controller tests to be authenticated.
Before the first describe statement add this:
let!(:admin) { FactoryGirl.create(:admin) }
before { subject.stub(current_user: admin, authenticate_user!: true) }
it should stub your authentication.
And one little trick for your happiness: add in your spec_helper.rb anywhere within
RSpec.configure do |config|
...
end
block this code:
config.include FactoryGirl::Syntax::Methods
and now you don't need to preface all factory_girl methods with FactoryGirl, so instead of FactoryGirl.create you can write just create.
I had some issues with sending confirmation emails in Devise. That's why I would like to write tests for this functionality. How could I do this, when I don't create my own mailers?
EDIT
I decided, that this should be enough:
it 'should send an email' do
user
put :complete, params
user.send(:send_confirmation_notification?).should == true
end
Please, let me know if I missed something.
Have you looked at the tests which have been written for Devise?
https://github.com/plataformatec/devise/blob/master/test/mailers/confirmation_instructions_test.rb
This worked for me if you want to have a more explicit test and actually test the email is sent with RSpec.
it 'sends a confirmation email' do
user = FactoryGirl.build :user
expect { user.save }.to change(ActionMailer::Base.deliveries, :count).by(1)
end
I cant find solution for my problem: cant login user in rspec test.
I tried this code, following by this link - https://github.com/plataformatec/devise/wiki/How-To:-Stub-authentication-in-controller-specs
require 'spec_helper'
require 'factory_girl_rails'
describe "ololo" do
it "blocks unauthenticated access" do
user = User.create(email: "lol#mail.com", password: "123456")
request.env['warden'].stub(:authenticate!).
and_throw(:warden, {:scope => :user})
visit "/tasks"
page.should have_content("Signed in successfully.")
end
end
/error/
Failure/Error: page.should have_content("Signed in successfully.")
expected there to be text "Signed in successfully." in "Task App Home Login You need to sign in or sign up before continuing. Sign in Email Password Remember me Sign upForgot
your password? Task App by Denys Medynskyi"
also tried this link - http://codingdaily.wordpress.com/2011/09/13/rails-devise-and-rspec/.
Devise wiki tutorial is also not working for me.
Please, I'm stuck hardly. Help me anyone.
Without seeing your error message, I guest one possible reason is you created hard record of User to test db. You may have run this test once, so in second time this creation fails because record with same email exists.
You've required FactoryGirl. Why don't you use FactoryGirl to create the fixture data?
In spec/factories/user.rb
FactoryGirl.define do
factory :user do
email: "lol#mail.com"
password "123"
end
end
In your test, write
FactoryGirl.create(:user)
Add
Comparing with the tut you lack two steps:
You need to use FactoryGirl to replace the hard created record, as writing above.
You need to execute the sign_in step before visit the private page.
Maybe you would be good with #2 only if you cleared the db after tests. Anyway factory data is recommended over hard data.
I have started my journey with TDD in Rails and have run into a small issue regarding tests for model validations that I can't seem to find a solution to. Let's say I have a User model,
class User < ActiveRecord::Base
validates :username, :presence => true
end
and a simple test
it "should require a username" do
User.new(:username => "").should_not be_valid
end
This correctly tests the presence validation, but what if I want to be more specific? For example, testing full_messages on the errors object..
it "should require a username" do
user = User.create(:username => "")
user.errors[:username].should ~= /can't be blank/
end
My concern about the initial attempt (using should_not be_valid) is that RSpec won't produce a descriptive error message. It simply says "expected valid? to return false, got true." However, the second test example has a minor drawback: it uses the create method instead of the new method in order to get at the errors object.
I would like my tests to be more specific about what they're testing, but at the same time not have to touch a database.
Anyone have any input?
CONGRATULATIONS on you endeavor into TDD with ROR I promise once you get going you will not look back.
The simplest quick and dirty solution will be to generate a new valid model before each of your tests like this:
before(:each) do
#user = User.new
#user.username = "a valid username"
end
BUT what I suggest is you set up factories for all your models that will generate a valid model for you automatically and then you can muddle with individual attributes and see if your validation. I like to use FactoryGirl for this:
Basically once you get set up your test would look something like this:
it "should have valid factory" do
FactoryGirl.build(:user).should be_valid
end
it "should require a username" do
FactoryGirl.build(:user, :username => "").should_not be_valid
end
Here is a good railscast that explains it all better than me:
UPDATE: As of version 3.0 the syntax for factory girl has changed. I have amended my sample code to reflect this.
An easier way to test model validations (and a lot more of active-record) is to use a gem like shoulda or remarkable.
They will allow to the test as follows:
describe User
it { should validate_presence_of :name }
end
Try this:
it "should require a username" do
user = User.create(:username => "")
user.valid?
user.errors.should have_key(:username)
end
in new version rspec, you should use expect instead should, otherwise you'll get warning:
it "should have valid factory" do
expect(FactoryGirl.build(:user)).to be_valid
end
it "should require a username" do
expect(FactoryGirl.build(:user, :username => "")).not_to be_valid
end
I have traditionally handled error content specs in feature or request specs. So, for instance, I have a similar spec which I'll condense below:
Feature Spec Example
before(:each) { visit_order_path }
scenario 'with invalid (empty) description' , :js => :true do
add_empty_task #this line is defined in my spec_helper
expect(page).to have_content("can't be blank")
So then, I have my model spec testing whether something is valid, but then my feature spec which tests the exact output of the error message. FYI, these feature specs require Capybara which can be found here.
Like #nathanvda said, I would take advantage of Thoughtbot's Shoulda Matchers gem. With that rocking, you can write your test in the following manner as to test for presence, as well as any custom error message.
RSpec.describe User do
describe 'User validations' do
let(:message) { "I pitty da foo who dont enter a name" }
it 'validates presence and message' do
is_expected.to validate_presence_of(:name).
with_message message
end
# shorthand syntax:
it { is_expected.to validate_presence_of(:name).with_message message }
end
end
A little late to the party here, but if you don't want to add shoulda matchers, this should work with rspec-rails and factorybot:
# ./spec/factories/user.rb
FactoryBot.define do
factory :user do
sequence(:username) { |n| "user_#{n}" }
end
end
# ./spec/models/user_spec.rb
describe User, type: :model do
context 'without a username' do
let(:user) { create :user, username: nil }
it "should NOT be valid with a username error" do
expect(user).not_to be_valid
expect(user.errors).to have_key(:username)
end
end
end