How to test mailer inside controller spec - ruby-on-rails

I need to check if my API method sends an email. Based on various threads on SO, i ended up with something like this:
require 'spec_helper'
describe Api::V1::IntervieweesController do
before(:each) do
#project = FactoryGirl.create(:project)
end
describe "POST /api/v1/interviewees/remind_code" do
before(:each) do
#interviewee = FactoryGirl.create(:interviewee, project: #project)
end
it "sends email with code" do
post :remind_code, interviewee: { email: #interviewee.email }
ActionMailer::Base.deliveries.last.to.should == [#interviewee.email]
end
end
end
The problem is, ActionMailer::Base.deliveries is allways nil, even if that method sends that email (when i'm running that method in a 'normal' way in my browser, outside of rspec tests). What's more interesting - it works fine in model specs. Do you have any idea what am i possibly doing wrong?
Rails v4
Ruby v2
rspec v3.0.0.beta1
Please have in mind that the controller's action i'm testing above works fine and sends that email on production/development.

Related

RSpec Integration Testing - Testing how many anchor tags

I have never dabbled outside of model testing when it comes to testing, and I am currently learning how to create my own user authentication instead of relying on Devise. It has been a little bit of time since I have worked with RSpec and not only would I like a little sanity check for syntax, but I can not figure out a way to confirm that my log in and sign up is indeed disappearing when a user logs in.
Here is my current users_logins_spec.rb
require 'rails_helper'
RSpec.describe "UsersLogins", type: :request do
before(:each) do
#user = FactoryGirl.build(:user)
end
it "login with invalid information" do
get login_path
expect(response).to render_template(:new)
post login_path, session: { email: "", password: "" }
expect(response).to render_template(:new)
expect(flash).to be_present
get root_path
expect(flash).not_to be_present
end
it "login with valid information" do
get login_path
post login_path, session: { email: #user.email, password: "password"}
expect(response).to redirect_to(#user)
follow_redirect!
expect(response).to render_template('users/show')
# expect(page).to have_selector('a', login_path)
end
end
Emphasizing the last test because that is the one that fails. I believe that if I were to put ID's on the tags that I want to check I would be able to circumvent the problem that I am having with methods that I understand. My intention is to learn how to manipulate my tests without having to find workarounds that change my code outside of the test, despite how little of a change that would be.
The other question is dealing with redirects. When I want to redirect to the #user url_path of #user, how does RSpec different when interpreting the call? I know that in Rails if I had something like
= link_to "Profile", current_user
it would automatically interpret it as
= link_to "Profile", user_path(current_user)
assuming my user resources within routes.rb.
If anyone can recommend some good tutorials for Rspec with Capybara for Integration and Feature testing that would be awesome, and any help/advice would be greatly appreciated. I am trying to make this as a Integration test instead of a feature test (which to my understanding those are kept within the requests directory and are "less readable" because they aren't so much as user stories but still are checking functionality of the site)
EDIT:
So I figured out part of my problem. I put in a debugger and was able to figure out that my user wasn't actually logging in correctly.
Here is the method that I am using to digest a password within the factory.
user.rb
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
factories.rb
FactoryGirl.define do
factory :user do
sequence(:id) { |n| n }
sequence(:name) { |n| "foo#{n}" }
email { "#{name}#example.com" }
password_digest User.digest('password')
end
end
The problem seems to be that my user login credentials are invalid and I am not exactly sure why.
FINAL EDIT - SOLVED
Okay, so I got it working. My problem with the user being incorrect was an easy fix. Instead of using password_digest within the factory I just did changed it to password and password_confirmation and it began the redirect. I originally had FactoryGirl.create(user) and have been switching between the two throughout testing, but in order for this to work with the confirmation it had to be create.
The next issue was actually with assert_select.
Here is the error:
NotImplementedError:
Implementing document_root_element makes assert_select work without needing to specify an element to select from.
I did end up finding a solution. Apparently this is with the latest version of RSpec and the solution that I had found was to set the document_root_element.
Within my spec/support I created a module
**spec/support/assert_select_root.rb
module AssertSelectRoot
def document_root_element
html_document.root
end
end
RSpec.configure do |config|
config.include AssertSelectRoot, :type => :request
end
I guess this was required for tests within spec/requests tests
Joe. You should keep in mind that FactoryGirl.build do not create database instance. So your Users table may be empty if you don't seed it before test.
I suggest you to use .create instead of .build.

Rspec controller test does not hit my controller action

I added an import method to a controller, and it works just fine when I test it manually from my website, but it is failing in rspec. Here is what my test looks like:
require 'spec_helper'
describe PropertiesController do
let!(:user) { FactoryGirl.create(:user) }
before :each do
sign_in user
end
describe "should upload user properties" do
before do
post :import, spreadsheet: fixture_file_upload("/files/property_upload_template.xlsx")
end
it "should have created records" do
expect(Property.count).to eq 3
# Some other assertions
end
end
end
When I add puts statements inside my import action, including on the very first line, none of them are apparently invoked. The test is generating no errors other than failing the assertions. Similarly, when I look at the test.log file, all that happens is the creation of my test user (and a devise confirmation email gets sent out), but it doesn't appear that the import action is ever hit. The test server seems to recognize the route fine, but it's not actually executing the action.
Is there something wrong with my test configuration?
I had been banging my head for a good couple hours, but I just figured it out. I needed to confirm the user in my user factory. I guess since I enabled the confirmable module in devise, and the user wasn't confirmed, it was silently not allowing me to authenticate...
... Would sure be nice if rspec/rails/devise generated some sort of error pointing me to the problem here.
For the sake of completeness, I'm adding in the code for confirming a user in the version of FactoryGirl at the time of that writing:
FactoryGirl.define do
factory :confirmed_user, :parent => :user do
after(:create) { |user| user.confirm! }
end
end

login_as with rspec fails first test

Following instructions from that Devise How-To page I'm trying to rebuild all my rspec test to bypass user signin process.
There are 2 methods that can be used for that:
sign_in from Devise - which can't be used with feature tests (Capybara)
login_as from Warden (Devise is build on top of it)
1st method at the first shot worked (all test passes) except those with Capybara so I decided to leave it for now.
2nd gives me some weird results - all passes except the first one (any which I place as the first in the file). It fails when I run just one of them. I've checked it with binding.pry and it fails as the user is not logged in and it redirects to the login page. Somehow first test triggers something that makes all the rest passing. I have no clue what's going on here.
I was using around hook before but it behaves really weird so I've changed that to set of before and after (at the same time it works much faster as it creates just one user at the begging rather than for every test). This is how it looks like now:
require 'spec_helper'
describe AlbumsController do
let(:album) { create(:album) }
before(:all) do
#user = create :user
end
before(:each) do
login_and_switch_schema #user
end
after(:all) do
destroy_users_schema #user
destroy_user #user
end
describe "GET #new" do
before { get :new }
it { expect(response).to render_template :new }
end
describe "GET #edit" do
before { get :edit, id: album }
it { expect(response).to render_template :edit }
end
...
and I've defined that helpers:
Warden.test_mode!
def login_and_switch_schema(user)
##request.env["devise.mapping"] = Devise.mappings[:user]
#sign_in :user, user
login_as(user, scope: :user)
Apartment::Database.switch(user.username)
end
def destroy_users_schema(user)
Apartment::Database.drop(user.username)
Apartment::Database.reset
end
def destroy_user(user)
User.destroy(user)
end
I would like to ask you for help.
I would try moving your before(:all) and after(:all) code into the before(:each) and after(:each). :all doesn't play well with let, DatabaseCleaner or in giving you a predictable order of execution for the first test executed.

Testing authentication with Sorcery and RSpec

I've spent far too long messing with this before asking for help. I can't seem to get RSpec and Sorcery to play together nicely. I've read through the docs on Integration testing with Sorcery and can post the login action properly, but my tests still doesn't think the user is logged in.
# spec/controllers/user_controller_spec
describe 'user access' do
let (:user) { create(:user) }
before :each do
login_user(user[:email], user[:password])
end
it "should log in the user" do
controller.should be_logged_in
end
end
And my login_user method
# spec/support/sorcery_login
module Sorcery
module TestHelpers
module Rails
def login_user email, password
page.driver.post(sessions_path, { email: email , password: password, remember_me: false })
end
end
end
end
The sessions controller handles the pages properly when I use them on the generated pages just fine. I tried outputting the results of the login_user method and it appears to properly post the data. How do I persist this logged in user through the tests? Does a before :each block not work for this? I'm just not sure where it could be running wrong and I'm pretty new to testing/RSpec so I may be missing something obvious. I'd appreciate any help.
Here's the output of the failed tests:
1) UsersController user access should log in the user
Failure/Error: controller.should be_logged_in
expected logged_in? to return true, got false
I just went through this yesterday. Here's what I did, if it helps.
Sorcery provides a test helper login_user that relies on a #controller object being available. This works great in controller specs, but doesn't work in integration tests. So the workaround in integration tests is to write another method (like the one you have above) to simulate actually logging in via an HTTP request (essentially simulating submitting a form).
So my first thought is that you should try renaming your method to login_user_post or something else that doesn't collide with the built-in test helper.
Another potential gotcha is that it looks to me like the Sorcery helper assumes that your user's password is 'secret'.
Here's a link to the built-in helper so you can see what I'm talking about:
https://github.com/NoamB/sorcery/blob/master/lib/sorcery/test_helpers/rails.rb
Good luck - I really like this gem except for this part. It is really only fully explained by patching together SO posts. Here's the code I use:
Integration Helper
module Sorcery
module TestHelpers
module Rails
def login_user_post(user, password)
page.driver.post(sessions_url, { username: user, password: password})
end
def logout_user_get
page.driver.get(logout_url)
end
end
end
end
Integration Spec (where user needs to be logged in to do stuff)
before(:each) do
#user = create(:user)
login_user_post(#user.username, 'secret')
end
Controller Spec (where the regular login_user helper works fine)
before(:each) do
#user = create(:user)
login_user
end
Note that login_user doesn't need any arguments if you have an #user object with the password 'secret'.
Did you try adding to spec/spec_helpers.
RSpec.configure do |config|
# ...
config.include Sorcery::TestHelpers::Rails::Controller
end
Nota that you need to include Sorcery::TestHelpers::Rails::Controller, not just Sorcery::TestHelpers::Rails.
Then you will be able to login_user from any controller specs like:
describe CategoriesController do
before do
#user = FactoryGirl::create(:user)
end
describe "GET 'index'" do
it "returns http success" do
login_user
get 'index'
expect(response).to be_success
end
end
end
The way you pass a password is probably wrong. It may be encrypted at this point. In provided example I will try to do this at first:
describe 'user access' do
let (:user) { create(:user, password: 'secret') }
before :each do
login_user(user[:email], 'secret')
end
it "should log in the user" do
controller.should be_logged_in
end
end
This seems to be very poorly documented. The above solutions did not work for me. Here's how I got it to work:
Check your sessions_url. Make sure it is correct. Also, check what params are necessary to log in. It may be email, username, etc.
module Sorcery
module TestHelpers
module Rails
def login_user_post(email, password)
page.driver.post(sessions_url, { email:email, password: password })
end
end
end
end
RSpec config:
config.include Sorcery::TestHelpers::Rails
Spec helper:
def app
Capybara.app
end
spec/controllers/protected_resource_spec.rb:
describe UsersController do
before do
# Create user
# Login
response = login_user_post( user.email, :admin_password )
expect( response.headers[ 'location' ]).to eq 'http://test.host/'
# I test for login success here. Failure redirects to /sign_in.
#cookie = response.headers[ 'Set-Cookie' ]
end
specify 'Gets protected resource' do
get protected_resource, {}, { cookie:#cookie }
expect( last_response.status ).to eq 200
end

Rspec2 controller testing with devise

I'm currently new to RSpec and trying to implement some Controller testing with RSpec
In my Rails app, I'm using Devise as my authentication system. My question is, When we test a controller which uses some authentication system (in my case Devise), what is the standard practice?
Is it
1 - to skip the authentication
or
2 - to authenticate the controller
as per the question, following is my controller
require File.dirname(__FILE__) + '/../spec_helper'
describe ProjectsController do
include Devise::TestHelpers
p "starting..."
before(:each) do
p "in before method"
#request.env["devise.mapping"] = Devise.mappings[:user]
sign_in Factory.create(:user)
end
it "should create a project" do
p "should create a project"
end
after(:each) do
#user.destroy unless #user.nil?
end
end
I can only see 'starting', But why its not going to "in before method" and "should create a project"
I'm using Rspec2 and Rails2 on Ubuntu.
Check this: Stubbing Devise in rSpec and Rails3.
Standard practice is not skipping authentication, but effectively making sure that a correct user is logged in (for devise).
Referring to your code: have you tried to create some real test? E.g. something as simple as
it "gets index" do
get :index
response.status.should be == 200
end
I am not sure why you are not seeing the print-statements. Either rspec skips the empty step (there is no real code), or because something else went wrong. But honestly, I am not even sure if using p inside rspec works.
A tool like rubymine allows you to easily debug your specs if you want to step into it (which imho is a better approach then the scattered p statements).

Resources