Rails Controller Tests for Captcha using Shoulda, Factory Girl, Mocha - ruby-on-rails

Can someone provide a strategy/code samples/pointers to test Captcha validations + Authlogic using Shoulda, Factory Girl and Mocha?
For instance, my UsersController is something like:
class UsersController < ApplicationController
validates_captcha
...
def create
...
if captcha_validated?
# code to deal with user attributes
end
...
end
In this case, how do you mock/stub using Shoulda / Factory Girl / Mocha to test valid and invalid responses to the Captcha image?
Appreciate your help,
Siva

I think it depends where captcha_validated? is defined, but you want to mock its return value and then write tests for each case. Something like this:
describe UsersController, "POST create" do
context "valid captcha" do
before do
SomeCaptchaObject.expects(:captcha_validated?).returns(true)
end
# ...
end
context "invalid captcha" do
before do
SomeCaptchaObject.expects(:captcha_validated?).returns(false)
end
# ...
end
end

I was able to solve with this setup:
class UsersControllerTest < ActionController::TestCase
context "create action" do
context "valid user with valid captcha" do
setup do
User.any_instance.stubs(:valid?).returns(true)
#controller.stubs(:captcha_validated?).returns(true)
post :create, :user => Factory.attributes_for(:user, :captcha => "blahblah")
end
should_redirect_to("user home") { user_path(#user) }
end
context "valid user with invalid captcha" do
setup do
User.any_instance.stubs(:valid?).returns(true)
#controller.stubs(:captcha_validated?).returns(false)
post :create, :user => Factory.attributes_for(:user, :captcha => "blahblah")
end
should_render_template :new
end
end
end
Thanks.

Related

CanCan::AccessDenied with factory_girl and cancan, How correctly to write the factory?

I am suffering for the third day, I can not understand why I do not pass the next test:
4) Error:
ArticlesControllerTest#test_should_get_index_if_admin:
CanCan::AccessDenied: You are not authorized to access this page.
test/controllers/articles_controller_test.rb:22:in `block in <class:ArticlesControllerTest>'
What am I doing wrong? help me please!
I have got old application (rails 4.2), with many fixtures data.
I try migrate my test environment from fixtures to factory_girl. So I'm new to this.
Now I'm using:
cancancan + devise
factory_girl + TestCase
My articles controller:
class ArticlesController < ApplicationController
load_and_authorize_resource
before_filter :authenticate_user!, except: [:show]
def index
#articles = Article.paginate(page: params[:page], per_page: 10).includes(:translations)
end
end
Ability.rb:
Class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
# Everybody
can :show, [Article]
if user.admin?
can :manage, Article
end
end
end
My factory article.rb is very simple:
FactoryGirl.define do
factory :article do
content "MyText"
factory :one_article
factory :two_article
end
end
My factory user.rb is simple too:
FactoryGirl.define do
factory :user do
sequence(:email) { |n| "user#{n}#mail.ru" }
password "password"
password_confirmation "password"
after(:create) {|u| u.roles_mask = 4}
profile
factory :valid_admin do
first_name "Administrator"
last_name "Administrator"
association :profile, factory: :admin_profile
after(:create) {|u| u.roles_mask = 2}
end
end
end
My articles controller test:
require 'test_helper'
class ArticlesControllerTest < ActionController::TestCase
include Devise::Test::ControllerHelpers
setup do
#article = create(:one_article)
#admin = create(:valid_admin)
end
test 'should get index if admin' do
sign_in #admin
ability = Ability.new(#admin)
assert ability.can? :index, Article
get :index
assert_response :success
assert_not_nil assigns(:articles)
end
end
Info by pry:
[1] pry(#<ArticlesControllerTest>)> sign_in #admin
=> [[20709], "9BET5RWNuJPrGHUFi86d"]
[2] pry(#<ArticlesControllerTest>)> ability = Ability.new(#admin)
=> #<Ability:0x0000000c3c5ff8
#rules=
[#<CanCan::Rule:0x0000000c3c5f80
#actions=[:show],
#base_behavior=true,
#block=nil,
.............<<Many lines>> ..............
[3] pry(#<ArticlesControllerTest>)> assert ability.can? :index, Article
=> true
[4] pry(#<ArticlesControllerTest>)> get :index
CanCan::AccessDenied: You are not authorized to access this page.
from /home/da/.rvm/gems/ruby-2.2.6#wenya/gems/cancancan-1.16.0/lib/cancan/ability.rb:217:in `authorize!'
Thanks in advance for your help!
This are the devise guideline, you need to create a method to log in the user as admin. This is the login_method you need to create
Controller tests (Test::Unit)
To sign in as admin for a given test case, just do:
class SomeControllerTest < ActionController::TestCase
# For Devise >= 4.1.1
include Devise::Test::ControllerHelpers
# Use the following instead if you are on Devise <= 4.1.0
# include Devise::TestHelpers
def setup
#request.env["devise.mapping"] = Devise.mappings[:admin]
sign_in FactoryGirl.create(:admin)
end
end
Note: If you are using the confirmable module, you should set a confirmed_at date inside the Factory or call confirm! before sign_in.
Here is the basics to prepare inside your Factory:
FactoryGirl.define do
factory :account do
email { Faker::Internet.email }
password "password"
password_confirmation "password"
confirmed_at Date.today
end
end

Reset Password testing with Rspec

i am using rails and want to write a test for password reset in Rspec. i am quite new to testing.
this is what i have done so far:
require 'rails_helper'
describe UsersController, type: :controller do
describe 'post #reset_password' do
let(:user) { create(:user) }
context "reset password" do
def do_request
patch :update_password
end
before { do_request }
it { expect(ActionMailer::Base.deliveries.count(1) }
end
end
end
every time i run this it gives ma an syntax error in
"it { expect(ActionMailer::Base.deliveries.count(1) } ".
i want to check whether the email successfully sent of not and if the user have key in the email.
Thanks!
1) you miss ) at last here so got syntax error
it { expect(ActionMailer::Base.deliveries.count(1) }
to
it { expect(ActionMailer::Base.deliveries.count(1)) }
2)
If you want to check total deliveries. you can try
it 'should send an email' do
ActionMailer::Base.deliveries.count.should == 1
end
also check sender
it 'renders the sender email' do
ActionMailer::Base.deliveries.first.from.should == ['notifications#domain.com']
end
Also check subject line
it 'should set the subject to the correct subject' do
ActionMailer::Base.deliveries.first.subject.should == 'Here Is Your Story!'
end
The problems you're having will most likely be fixed by writing better tests.
Here's generally how you would write tests for something like this.
Lets suppose in your routes file you have a post route that looks something like this
# config/routes.rb
post "/user/:id/reset_password", to: "users#reset_password"
And your User controller looks something like this
# app/controllers/users_controller.rb
class UsersController
...
def reset_password
user = User.find(params[:id])
user.reset_password!
SomeMailClass.email_reset_instructions(user)
end
end
and your User.rb model looks something like this
# app/models/user.rb
class User < ActiveRecord::Base
def reset_password!
update!(password: nil) # or whatever way you want/need to reset the password
end
end
and you have some type of mailing class to send your email
# app/models/some_mail_class.rb
class SomeMailClass
def self.email_reset_instructions(user)
# do something to send email...
end
end
The way you would go about testing this in the controller would be
# spec/controllers/users_controller_spec.rb
require 'rails_helper'
describe UsersController, type: :controller do
it "#reset_password" do
user_id = double(:user_id)
user = double(:user)
expect(User).to receive(:find).with(user_id).and_return(user)
expect(user).to receive(:reset_password!).and_return(true)
expect(SomeMailClass).to receive(:email_reset_instructions).with(user)
post :reset_password, id: user_id
end
end
But you shouldn't stop there. Because the implementation of the newly made method reset_password! and the SomeMailClass has yet to be tested. So you would write model/unit tests like this for them
# spec/models/user_spec.rb
require "rails_helper"
describe User do
it ".reset_password!" do
user = User.create(password: "foo")
expect(user.password).to eq "foo"
user.reset_password!
expect(user.password).to eq nil
end
end
Then you might install vcr and factory_girl gems and use them like so to test your mailer
# spec/models/some_mail_class_spec.rb
require "rails_helper"
describe SomeMailClass do
VCR.use_cassette "email_reset_instructions" do |cassette|
it ".email_reset_instructions" do
user = FactoryGirl.create(:user)
SomeMailClass.email_reset_instructions(user)
# you can write some expectations on the cassette obj to test.
# or you can write whatever expectations you need/desire
end
end
end
And in the end if there was something happening on the front end that a user would click that made this post request you would write a feature test for it as well.
Hope this helps!

Rails Controller testing

I am doing the thoughtbot intro to testing program. Im not sure how to test for what they want.
Below is my test.
require "rails_helper"
describe PeopleController do
describe "#create" do
context "when person is valid" do
it "redirects to #show" do
post :create, FactoryGirl.build_stubbed(:person)
expect(response).to redirect_to(show_people_path)
end
end
context "when person is invalid" do
it "redirects to #new" do
pending "create this test"
end
end
end
end
I am of course using factory girl. I have tried several methods. I really don't know hoe to test this controller.
Any insights would be great.
I would create an 'invalid' person using the FactoryGirl, and send it as a parameter to the post :create.
To create an invalid person record, why don't you use nested factories in FactoryGirl? Depending on the validation in your model, you can simply do something like:
spec/factories/person.rb
FactoryGirl.define do
factory :person do
...
factory :invalid_person do
...
email nil
...
end
end
end
in your test
context "when person is invalid" do
it "redirects to #new" do
post :create, FactoryGirl.build_stubbed(:invalid_person)
expect(response).to redirect_to action: :new
end
end

How do you "nest" or "group" Test::Unit tests?

RSpec has:
describe "the user" do
before(:each) do
#user = Factory :user
end
it "should have access" do
#user.should ...
end
end
How would you group tests like that with Test::Unit? For example, in my controller test, I want to test the controller when a user is signed in and when nobody is signed in.
You can achieve something similar through classes. Probably someone will say this is horrible but it does allow you to separate tests within one file:
class MySuperTest < ActiveSupport::TestCase
test "something general" do
assert true
end
class MyMethodTests < ActiveSupport::TestCase
setup do
#variable = something
end
test "my method" do
assert object.my_method
end
end
end
Test::Unit, to my knowledge, does not support test contexts. However, the gem contest adds support for context blocks.
Shoulda https://github.com/thoughtbot/shoulda although it looks like they've now made the context-related code into a separate gem: https://github.com/thoughtbot/shoulda-context
Using shoulda-context:
In your Gemfile:
gem "shoulda-context"
And in your test files you can do things like (notice the should instead of test:
class UsersControllerTest < ActionDispatch::IntegrationTest
context 'Logged out user' do
should "get current user" do
get api_current_user_url
assert_response :success
assert_equal response.body, "{}"
end
end
end

RSpec-2 and Devise

i create a customized devise registration controller and i want to test it with rspec.
I've tried it with a very simple test :
it "creates a new parent" do
Parent.should receive(:new)
post :create
end
but i get this exception:
Failures:
1) Parent::RegistrationsController POST create creates a new parent
Failure/Error: post :create, { :commit => "Daftar",
uncaught throw `warden'
# /home/starqle/.rvm/gems/ree-1.8.7-2010.02/gems/devise-1.1.3/lib/devise/hooks/timeoutable.rb:16:in `throw'
# /home/starqle/.rvm/gems/ree-1.8.7-2010.02/gems/devise-1.1.3/lib/devise/hooks/timeoutable.rb:16
I already put this line within my test:
describe Parent::RegistrationsController do
include Devise::TestHelpers
end
I also already put this line:
request.env["devise_mapping"] = Devise.mappings[:parent]
anybody have ideas to solve this problem?
My previous answer is a little confusing. sorry.
Updated answer: root cause is user is not "confirmed" before "sign in".
#user.confirm!
sign_in #user
then everything is fine.
I am fresher in ruby.
I am using rails 3 with devise and factory girl.
I was searching for how to authenticate user for rspec.
I was stucked at before_filter: authenticate_user! in controller.
Finally I got solution (thanks to Siwei Shen)
What I am doing is
include TestHelpers in spec/spec_helper.rb
2.
require 'spec_helper'
describe StudentsController do
before(:each) do
#user = Factory.create(:user) #:user from factory girl with admin privilages
#request.env['devise.mapping'] = :user
#user.confirm!
sign_in #user
end
it "can get index of student" do
get :index
response.should be_suclogin_as #user
end
it "can create student" do
#in student model : validates :name, :presence=> true
post :create, :student => {name => "student1" }
answer = Student.find_by_name("student1")
answer.name.should == "student1"
end
end

Resources