how do you stub a local variable in an rspec controller test - unit-testing

I've just implemented OmniAuth (using Ryan Bates' Screencast http://asciicasts.com/episodes/235-omniauth-part-1) and am writing Rspec tests for the functionality and have ran into trouble testing the authentifications#create action. I'm at quite a loss as to how to test this one -- in particular how to stub the local variable omniauth. No matter what I try I keep can't get any tests to work.
Taking a cut down version of the action, how would you test that a new is called on User for example
#cut down version of the authentifications controller code I am attempting to test
def create
omniauth = request.env["omniauth.auth"]
authentification = Authentification.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
....
user = User.new
....
end
#example test
it "should create a new user" do
subject.stub_chain(:request,:env) {{"omniauth.auth" => {'provider' =>1, 'uid' => 2}}}
User.should_receive(:new)
post :create
end

I Did that :
class SessionsController < ApplicationController
def create
#user = User.find_by_auth_hash(auth_hash)
end
def auth_hash
request.env['omniauth.auth']
end
end
describe SessionsController do
it 'should allow login' do
controller.stub!(:auth_hash).and_return({'provider' => 'twitter', 'uid' => '1234'})
get :create, :provider => 'twitter'
assigns(:user).should_not be_nil
end
end
Hope that helps.

Related

Using log_in_as helper module in ActionDispatch::IntegrationTest

I started learning Rails using Michael Hartl's "Ruby on Rails Tutorial", then I started to customize some classes to suit my own needs.
At this time, I have 2 main classes I can manage with my application:
"users": admin user can list, create destroy them
"apps": admin can list, create destroy them
I recycled most from the tutorial, and managed to make the application working for both of them.
"users" integration testing works fine.
When started to develop the "apps" testing, I found there is something that makes login fail and I'm really stucked on this.
Here is the "users" integration test (it works):
require 'test_helper'
class UsersCreateTest < ActionDispatch::IntegrationTest
fixtures :all
def setup
#admin = users(:admin)
end
test "user create with valid information" do
log_in_as(#admin)
assert is_logged_in?
get new_user_path()
...
assert_difference 'User.count', 1 do
post users_path, user:{ username: "newuser",
email: "user#example.com",
password: "pippo1",
password_confirmation: "pippo1",
...
}
follow_redirect!
end
...
end
end
Here is the test to create a new "app" (does not work):
require 'test_helper'
class AppsCreateTest < ActionDispatch::IntegrationTest
fixtures :all
def setup
#admin= users(:admin)
#app= apps(:app1)
end
test "create app with valid information" do
log_in_as(#admin)
assert is_logged_in?
get new_app_path()
...
assert_difference( 'App.count', 1) do
post apps_path, app: { appname: "newapp",
desc: "short descprition here",
...
}
follow_redirect!
end
end
end
Finally, here is the "test_helper.rb" part, which comes straight from the Hartl Tutorial:
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
def is_logged_in?
!session[:user_id].nil?
end
# Logs in a test user.
def log_in_as(user, options = {})
password= options[:password] || 'pippo1'
if integration_test?
post "/login", session: {
username: user.username,
password: password }
else
session[:user_id] = user.id
end
end
private
# Returns true inside an integration test.
def integration_test?
defined?(post_via_redirect)
end
end
The "app" test fails when it tries to do post operation in "log_in_as".
It seems that the "user" test inherits some method while the "app" test doesn't.
Any suggestion is appreciated.

Ruby on Rails Pundit's current_user is nil in integration test

I'm using the gems pundit and devise. I have a delete link that only shows up if you are an admin. I have an integration test that I would like to verify that the delete link only shows up for admins.
test 'comment delete link shows when it should' do
log_in_as #admin
get movie_path(#movie)
assert_select 'a[href=?]', movie_comment_path(comments(:one), #movie.id)
end
My test_helper.rb looks like this:
...
class ActiveSupport::TestCase
...
def log_in_as(user, options = {})
password = options[:password] || 'password'
if integration_test?
post user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
else
Devise::TestHelpers.sign_in user
end
end
private
# Returns true inside an integration test.
def integration_test?
defined?(post_via_redirect)
end
end
The response.body looks all right, but indeed there is no delete link. There is one when I run the development server and visit the page myself. I've narrowed this down to the current_user that pundit uses in the policies is being passed in with a value of nil. This is my comment_policy.rb:
class CommentPolicy
attr_reader :current_user, :comment
def initialize(current_user, model)
#current_user = current_user
#comment = model
end
def create?
if #current_user
#current_user.member? or #current_user.content_creator? or #current_user.moderator? or #current_user.admin?
end
end
def destroy?
if #current_user
#current_user == #comment.user or #current_user.moderator? or #current_user.admin?
end
end
end
As a closing remark, I've heard that Rails 5 has opted for integration tests instead of controller tests as we know them from Rails 4 for the default type of tests to be generated for our controllers. If this is the case, devise would be a heck of a lot more useful out of the box when using Rails 5 if the sign_in/sign_out helpers that work in controller tests were made to work in integration tests as well. But would I still have this issue of pundit not knowing what current_user is? I'm assuming this all works fine in controller tests because the current_user is scoped to controllers? Any and all light shed on this topic is much appreciated, but I would really like to figure out how to get integration tests to work with this setup because I have about a billion I want to write right now.
Not that it totally matters, but does it need to be using current_user in the policy or can it just use user in the policy. By this I mean according to the elabs/pundit README on Github I would just use #user and user everywhere instead of current_user. Read the README if I confused you.
Additionally the nil for current_user typically occurs when you don't have a valid CSRF token for your request. When you do this on the website manually by going to localhost:3000 or w/e you are first performing a get on the login path before doing the post on the login path with your credentials. In your integration test I don't seem to see where you are performing that get in order to get the CSRF for your session.
Hope this helps!!!

Getting tripped up by verify_partial_doubles with rails 4 and rspec 3

I'm using authlogic for my user authentication and in my ApplicationController I have "current_user", "current_user_session", etc. defined and set as helper_methods.
I have an extremely simple view spec for my main index:
RSpec.describe "main/index.html.erb", :type => :view do
context "when not logged in" do
before do
allow(view).to receive(:current_user).and_return(nil)
end
it "has an h1" do
render
expect(rendered).to include('h1')
end
end
end
The problem is that if "mocks.verify_partial_doubles = true" in my config then this causes an impressively massive error as it dumps an entire object and then says at the bottom:
1) main/index.html.erb when not logged in has an h1
Failure/Error: allow(view).to receive(:current_user).and_return(nil)
#<#<Class:0x00000104c249d0>:.........
#rendered_views={}>> does not implement: current_user
Of course, it is recommended that verify_partial_doubles is set to true, but in doing so this breaks. I pulled this straight from the documentation:
https://www.relishapp.com/rspec/rspec-rails/v/3-1/docs/view-specs/view-spec#passing-view-spec-that-stubs-a-helper-method
If the method appears in ApplicationHelper it'll work. But if it's in ApplicationController and defined as a helper_method there's no such luck:
helper_method :current_user, ...
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.record
end
I want the protection that verify_partial_doubles provides, how can I work around this?
This is a known issue and the only way to get it working is to extract the methods into a module and include it in your view helpers and the controller.
More information at: https://github.com/rspec/rspec-rails/issues/1076
You can disable double verification for views as follows:
RSpec.configure do |config|
config.before(:each, type: :view) do
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = false
end
end
config.after(:each, type: :view) do
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
end
end
This way you'll be able to keep stubbing view methods with:
allow(view).to receive(:current_user).and_return(nil)
More information at: https://github.com/rspec/rspec-rails/issues/1076

Rails 4:register a person in my rails app through voice call verification

Can you please share that how can i make a outbound call to a person who want to register in my Rails 4 application and make a verification through his voice(in short i want to register a person in my rails app through voice call verification), Is that possible with twillo or suggest some other way to do it?
Please suggest the step by step or suggest some tutorial/doc/video
https://github.com/twilio/twilio-ruby
and
https://www.twilio.com/docs/quickstart/ruby/twiml
Call and ask for verification:
require 'twilio-ruby'
# build up a response
response = Twilio::TwiML::Response.new do |r|
r.Say 'hello there, enter verification code', :voice => 'alice'
r.Dial :callerId => '+14159992222' do |d|
r.Gather :numDigits => '5', :action => "/verify_code?id=#{id}", :method => 'get' do |g|
g.Say 'Verified thankyou.' if verified?
else
# hangup or set behaviour
end
end
end
end
def verify_code
#verification = Verification.where(verification_code: params[:id]).first
if #verification
#activate
else
# Handle errors
end
end
Finally in your model you'll need code that generates the verification code, and verifies if it is confirmed
class Verification < ActiveRecord::Base
before_create -> { self[:verification_code] = rand.to_s[2..5] }
end

ActiveModel::ForbiddenAttributesError + cancan + rails 4 + model with scoped controller

I m using cancan(1.6.10) with rails 4.0.0. I have a model called 'App'(not scoped) and a controller Admin::AppsController(its scoped. ie app/controllers/admin/apps_controller).
the controller code is as
class Admin::AppsController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource class: App
def index
end
#CRUD methods and some other custom methods
...
private
def app_params
params.require(:app).permit(:name, :description, :author, :url_path, :validated, :active, :version)
end
end
I m getting error when i try to create a 'app'.
ActiveModel::ForbiddenAttributesError - ActiveModel::ForbiddenAttributesError:
activemodel (4.0.0) lib/active_model/forbidden_attributes_protection.rb:21:in `sanitize_for_mass_assignment'
I added
before_filter do
resource = controller_path.singularize.gsub('/', '_').to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end
as specified in https://github.com/ryanb/cancan/issues/835#issuecomment-18663815 but still getting the above error.
Using name spaces. Please try to change your code to this one below. I had same issue after #JiriKolarik suggested his solution to work with name spaces. I hope it helps.
before_filter do
resource = controller_name.singularize.to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end
if you use this workflow
before_filter do
resource = controller_path.singularize.gsub('/', '_').to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end
then your params method should look like this
def admin_app_params
params.require(:admin_app).permit(:name, :description, :author, :url_path, :validated, :active, :version)
end
The reason why, it's because form generators (form_form, simple_form) generate params with namespace_resource
So if you have Blog::Post, form generator will create params like this
{ "blog_post"=>{"title"=>"Post"}, "commit"=>"Create", "action"=>"create", "controller"=>"blog/posts", "locale"=>"en"}
And this is how before filter works:
before_filter do
resource = controller_path.singularize.gsub('/', '_').to_sym # => 'blog/posts' => 'blog/post' => 'blog_post' => :blog_post
method = "#{resource}_params" # => 'blog_post_params'
params[resource] &&= send(method) if respond_to?(method, true) # => params[:blog_post]
end
If you need read :blog_post from params, solution above will not work. If you need read :post from params, then this solution will not work, if your controller will be blog/post
cancan just does not work with the strong parameter. While there is a new gem cancancan which works well without any code change.