I am currently having trouble testing for destroying users generated with devise.
require 'test_helper'
class UserTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
def setup
#user = User.new(email: "user#example.com")
end
test "Should delete User"do
#user.save
assert_difference 'User.count', -1 do
#user.destroy
end
end
end
Currently the tests
fail and show that the difference was 0. I was wondering how I could test this (even though I know user.destroy works when I run it in a rails console).
All the best
D
I think, there is problem with saving the user #user.save. This is due the validation error. Default Devises User has a password as a mandatory field.
Try changing:
def setup
#user = User.new(email: "user#example.com")
end
to:
def setup
#user = User.new(email: "user#example.com",
password: "SuperS3cret")
end
The #user should be properly saved, and it make #user.destroy to work, and assert_difference to pass.
Good luck!
Related
In mailer of rails, as I know all method will be class method.
But I can not test my mailer method called:
user_mailer_spec.rb:
it "should call send_notifition method" do
#user = FactoryGirl.build(:user)
notify_email = double(:send_notifition)
expect(UsersMailer.new).to receive(:notify_email).with(#user)
#user.save
end
user_mailer.rb:
def notify(user)
mail to: user.email, subject: "Example"
end
user.rb:
after_commit :send_notifition
private
def send_notifition
UsersMailer.notify(self)
end
The above codes will not pass but when I change notifition to self.notifition, it pass:
def self.notify(user)
mail to: user.email, subject: "Example"
end
First of all, I'd like to point you to an awesome gem for testing emails: https://github.com/email-spec/email-spec.
I think the problem is that you're asserting on UsersMailer.new, thus putting a mock on a different instance than the one then being instantiated by the User model. I generally test emails like this without any issues:
it "should call send_notifition method" do
#user = FactoryGirl.build(:user)
mail = double(:mail)
expect(UsersMailer).to receive(:notify_email).with(#user).and_return(mail)
expect(mail).to receive(:deliver_later) # or deliver_now, if you don't use a background queue
#user.save
end
Note how I'm doing expect(UsersMailer) instead of expect(UsersMailer.new) and also take not that I'm asserting that the email is actually delivered (I think a deliver statement is missing in your code).
Hope that helps.
Solved:
Thank you #Clemens Kofler for supporting.
I have many mistaking in my code:
First: No need to install gem "email_spec", and change user.rb file
from
after_commit :send_notifition
private
def send_notifition
UsersMailer.notify(self)
end
to
after_commit :send_notifition
private
def send_notifition
UsersMailer.notify(self).deliver
end
Second: Change user_mailer_spec.rb file
from
it "should call send_notifition method" do
#user = FactoryGirl.build(:user)
expect(#user).to receive(:send_notifition)
notify_email = double(:send_notifition)
expect(UsersMailer.new).to receive(:notify_email).with(#user)
#user.save
end
to
it "should call send_notifition_mail_if_created_new_hospital method" do
#user = FactoryGirl.build(:user)
# I don't know why "expect(#user).to receive(:send_notifition)" not passed here
mail = double(:mail)
expect(UsersMailer).to receive(:notify_email).with(#user).and_return(mail)
allow(mail).to receive(:deliver)
#user.save
end
Finally: config mailer in config/environments/test.rb for test environment can use mailer (because spec run in test env)
All the other answers are wrong or out of date. I've tried several things. This is what I have:
require 'test_helper'
class DealsControllerTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
setup do
#deal = deals(:one)
#user = users(:one)
# https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-(and-RSpec)
##request.env["devise.mapping"] = Devise.mappings[:one]
#sign_in #user
#log_in_as #user
ApplicationController.allow_forgery_protection = false
post user_session_path, params: {user: {email: #user.email, password: #user.password}}
assert_select "p", "Invalid Email or password."
assert_response :redirect ############ line 16
#assert_text "Invalid Email or password."
assert_redirected_to root_url
end
Failure:
DealsControllerTest#test_should_destroy_deal [C:/Users/Chloe/workspace/fortuneempire/test/controllers/deals_controller_test.rb:16]:
Expected response to be a <3XX: redirect>, but was a <200: OK>
Since it passes the "Invalid email/password" test, that means the user is NOT logging in!
I tried these answers:
How to sign in a user using Devise from a Rails console?
How to sign_in for Devise to Test Controller with Minitest
https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-(and-RSpec)
Rails: Capybara can't log in user with Devise fixture data
This worked
require 'test_helper'
class DealsControllerTest < ActionDispatch::IntegrationTest
include Warden::Test::Helpers
setup do
#deal = deals(:one)
#user = users(:one)
# https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara
#user.confirmed_at = Time.now
#user.save
login_as(#user, :scope => :user)
end
teardown do
Warden.test_reset!
end
Hi I am implementing a method to delete a user account in my web application. My controller:
class UsersController < ApplicationController
before_filter :set_current_user
def user_params
params.require(:user).permit(:user_id, :first_name, :last_name, :email, :password, :password_confirmation)
end
def delete_account
#user = User.find_by_id(params[:id])
if #user.present?
#user.destroy
flash[:notice] = "User Account Deleted."
end
redirect_to root_path
end
def destroy
User.delete(:user_id)
redirect_to root_path
end
end
My rspec:
require 'spec_helper'
require 'rails_helper'
require'factory_girl'
describe UsersController do
describe "delete account" do
before :each do
#fake_results = FactoryGirl.create(:user)
end
it "should call the model method that find the user" do
expect(User).to receive(:find).with(params[:id]).and_return (#fake_results)
end
it "should destroy the user account from the database" do
expect{delete :destroy, id: #fake_results}.to change(User, :count).by(-1)
end
it "should redirect_to the home page" do
expect(response).to render_template(:home)
end
end
end
The first error is
Failure/Error: expect(User).to receive(:find).with(params[:id]).and_return (#fake_results)
NameError:undefined local variable or method `params' for #<RSpec::ExampleGroups::UsersController::DeleteAccount:0x00000007032e18>
I know what this error means but I don't know how to correct it. How can I pass the user id from the controller to rspec?
The second error is:
Failure/Error: expect(response).to render_template(:home)
expecting <"home"> but rendering with <[]>
I think there is something wrong with my controller method. It should redirect to the home page but it doesn't.
params is not available in your tests, it's available in your controller.
Looks like you create a test user in your test:
#fake_results = FactoryGirl.create(:user)
Then, you can use the id of this test user (#fake_results.id) instead of trying to use params[:id]:
expect(User).to receive(:find).with(#fake_results.id).and_return (#fake_results)
Although, you may want to change the name from #fake_results to something more meaningful e.g. test_user or so.
However, this should fix both of your problems as your second problem is there because of the first problem. As it's failing to delete the user in the first place, it's not being redirected to the root path and hence the home template is not rendering.
I want the simplest example to test the validity of attributes of devise's actions for new registrations (email, password, password confirmation), sign_in and forgot_password. The internet is full of RSpec ones but I want to go native with what Rails 4.2 gives me and there is absolutely nothing.
I am stuck to the default implementation:
require 'test_helper'
class RegistrationsControllerTest < ActionController::TestCase
def setup
#controller = RegistrationsController.new
#request = ActionController::TestRequest.new
#response = ActionController::TestResponse.new
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = Registrations.new(username: "John", email: "myemail#email.com")
end
end
I know this isn't too much but I am making my first steps in TDD, so please don't shoot !
How do I check for the validity of a user's attributes, for example a nil email or a password of 100 characters ?
You can check with rails in-build minitest
I have assumed you have overriding devise's registration_controller.
In tdd for controller you can write test cases for action.
Check modified code :-
require 'test_helper'
class RegistrationsControllerTest < ActionController::TestCase
include Devise::TestHelpers # for including devise's actions
def setup # this set up default settings for controller
#controller = RegistrationsController.new
#request = ActionController::TestRequest.new
#response = ActionController::TestResponse.new
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = Registrations.new(username: "John", email: "myemail#email.com")
end
setup do # this used for setting global variable used in test file
#user= users(:one) # users is the fixture file of test in which you can set default data for test environment.
end
test "should create user" do # then you test cases for controller
sign_in users(:one)
post :create, users:{email:'test#test.com, password:'XXXX'...}# you can pass arguments for create method. Please check it once, i am not sure about names
assert_response :success
end
end
Rails minitest provides some assertion.
I'm (finally) wiring CanCan / Ability into my app, and I've started by writing the RSpec tests. But they're failing — my Abilities appear to be overly permissive, and I don't understand why.
First, the Ability class. The intention is that non-admin users can manage only themselves. In particular, they cannot look at other users:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # create guest user if needed
if (user.has_role?(:admin))
can(:manage, :all)
else
can(:manage, User, :id => user.id)
end
end
end
The RSpec tests:
require 'spec_helper'
require 'cancan/matchers'
describe Ability do
before(:each) do
#user = User.create
end
describe 'guest user' do
before(:each) do
#guest = nil
#ability = Ability.new(#guest)
end
it "should_not list other users" do
#ability.should_not be_able_to(:read, User)
end
it "should_not show other user" do
#ability.should_not be_able_to(:read, #user)
end
it "should_not create other user" do
#ability.should_not be_able_to(:create, User)
end
it "should_not update other user" do
#ability.should_not be_able_to(:update, #user)
end
it "should_not destroy other user" do
#ability.should_not be_able_to(:destroy, #user)
end
end
end
All five of these tests fail. I've read the part of Ryan's documentation where he says:
Important: If a block or hash of
conditions exist they will be ignored
when checking on a class, and it will
return true.
... but at most, that would only explain two of the five failures. So clearly I'm missing something fundamental.
I would expect this to work:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # create guest user if needed
if (user.has_role?(:admin))
can(:manage, :all)
elsif user.persisted?
can(:manage, User, :id => user.id)
end
end
end
I'm not sure what the behavior is defined to be if you pass :id => nil, which is what happens in the guest case, but at any rate, if you don't want the guest to access the list view, you shouldn't call can :manage, User for that user at all.
In general, I find that assigning user ||= User.new to make the ability harder to reason about.
Hey, apparently this should work, but some refactoring would help you to find the issue:
require 'spec_helper'
require 'cancan/matchers'
describe Ability do
before(:each) { #user = User.create }
describe 'guest user' do
before(:each) { #ability = Ability.new(nil) }
subject { #ability } # take advantage of subject
it "should not be an admin user" do
#user.should_not be_admin
#user.should be_guest
end
it "should_not show other user" do
should_not be_able_to(:read, #user)
end
it "should_not create other user" do
should_not be_able_to(:create, User)
end
it "should_not update other user" do
should_not be_able_to(:update, #user)
end
it "should_not destroy other user" do
should_not be_able_to(:destroy, #user)
end
end
end
Note that also I removed this example #ability.should_not be_able_to(:read, User).
Hope it helps you.
I've got this bad habit of answering my own questions, but I give props to #jpemberthy and #Austin Taylor for pointing me in the right direction. First (and this is cosmetic), I added this to my User model:
class User
...
def self.create_guest
self.new
end
def guest?
uninitialized?
end
end
and cleaned up my Abilities model accordingly:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.create_guest
if (user.admin?)
<admin abilities here>
elsif (user.guest?)
<guest abilities here>
else
<regular user abilities here>
end
end
end
But the real fix was in my RSpec tests. Since User has validations on email and password fields, my original code of:
before(:each) do
#user = User.create
end
was failing, thus creating an uninitialized #user. Since the :id field was nil, the Ability clause:
can(:manage, User, :id => user.id)
was succeeding with a guest user because nil == nil (if that makes sense). Adding the required fields to satisfy the User validations made (almost) everything work.
Moral: just as #jpemberthy suggested in his code, always include a test to make sure your user objects have the privileges that they are supposed to! (I still have another question regarding CanCan, hopefully less boneheaded than this one, appearing in a StackOverflow topic near you...)