how can you test the 'catch all route' using ordinary controller tests? - ruby-on-rails-3

Note: As per RafaeldeF.Ferreira's suggestion, this question has been heavily edited since its original form.
My JSON-based app needs to return something sensible when given a bad route. We already know that the following rescue_from ActionController::RoutingError doesn't work in Rails 3.1 and 3.2:
# file: app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
rescue_from ActionController::RoutingError, :with => :not_found
...
end
(This is well documented in https://github.com/rails/rails/issues/671.) So I implemented what José Valim describes in this blog entry (item 3), and details are provided below.
But testing it has been problematic. This controller rspec test:
# file: spec/controllers/errors_controller.rb
require 'spec_helper'
require 'status_codes'
describe ErrorsController do
it "returns not_found status" do
get :not_found
response.should be(StatusCodes::NOT_FOUND)
end
end
fails with:
ActionController::RoutingError: No route matches {:format=>"json", :controller=>"sites", :action=>"update"}
Yet this integration test calls ErrorsController#not_found and succeeds:
# file: spec/requests/errors_spec.rb
require 'spec_helper'
require 'status_codes'
describe 'errors service' do
before(:each) do
#client = FactoryGirl.create(:client)
end
it "should catch routing error and return not_found" do
get "/v1/clients/login.json?id=#{#client.handle}&password=#{#client.password}"
response.status.should be(StatusCodes::OK)
post "/v1/sites/impossiblepaththatdoesnotexist"
response.status.should be(StatusCodes::NOT_FOUND)
end
end
So: Is there a way to test the 'catch all route' using ordinary controller tests?
implementation details
If you want to see the implementation, here are the relevant code snippets
# config/routes.rb
MyApp::Application.routes.draw do
... all other routes above here.
root :to => "pages#home"
match "/404", :to => "errors#not_found"
end
# config/application.rb
module MyApp
class Application < Rails::Application
config.exceptions_app = self.routes
...
end
end
# config/environments/test.rb
MyApp::Application.configure do
...
config.consider_all_requests_local = false
...
end
# app/controllers/errors_controller.rb
class ErrorsController < ApplicationController
def not_found
render :json => {:errors => ["Resource not found"]}, :status => :not_found
end
end

Related

Rspec Devise Sign in

I trying to sign in my admin so as to test the controller specs in rspec
I did my best to follow the instruction mention over here but it seem that I missed something because of which the admin is still not able to login resulting in my controller spec to fail
Here my code
## spec/spec_helper.rb
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
end
## spec/support/controller_macros.rb
module ControllerMacros
def login_admin
before(:each) do
#request.env['devise.mapping'] = Devise.mappings[:admin]
admin = Admin.create(first_name: 'Admin',
last_name: 'User',
email: 'admin#poaster.me',
password: 'admin1234',
password_confirmation: 'admin1234',
active: true
)
admin.confirmed_at = Time.now
admin.confirm!
sign_in admin
## Did an idependent test over here using binding.pry not sure why
# binding.pry
# session => {"warden.user.admin.key"=>[[27], "$2a$04$6KicZPZUvYxOBiMadVyIqe"]}
# request['env'].warden.user => nil
# request['env'].warden.authenticated? => false
end
end
end
The output that see in session inside my controller spec
{"flash"=>
#<ActionDispatch::Flash::FlashHash:0x007f9bee570738
#closed=false,
#flashes={:alert=>"You have to confirm your account before continuing."},
#now=nil,
#used=#<Set: {}>>}
Controller Spec
## spec/controllers/users_controller_spec.rb
login_admin
describe UsersController do
context 'GET users/index' do
it 'render users index pages' do
get :index
#binding.pry
#session => {"flash"=> #<ActionDispatch::Flash::FlashHash:0x007f9bee570738 #closed=false,#flashes={:alert=>"You have to confirm your account before continuing."},#now=nil,#used=#<Set: {}>>}
expect(response).to render_template('index')
end
end
end
Controller Code
## app/controllers/users_controller.rb
class UsersController < ApplicationController
before_filter :authenticate_admin!
before_filter :require_admin?, :only => [:deactivate, :activate, :index]
def index
page = params[:page] || 1
#users = User.includes(:company).where(roles_mask: 1) params[:page])
#users = #users.page(page).per(10)
end
end
Found it seem the issue was because I was doing this
admin.confirmed_at = Time.now
I turn using only
admin.confirm! plainly work out of box

How do I emulate logging in for controller tests?

I have a SearchesController that requires a user to be logged in before it will do its thing.
I'd like to write an rspec helper function login to emulate logging in for controller tests. (NB: I will handle integration / requests specs separately.) My attempts so haven't worked: the logged_in? method in ApplicationController returns false.
The question: how do I write the 'login' helper?
Here's the RSpec controller test:
# file: spec/controllers/searches_controller_spec.rb
require 'spec_helper'
require 'controllers_helper'
describe SearchesController do
include ControllersHelper
describe "GET index" do
it 'without login renders login page' do
get :index
response.should redirect_to(login_path)
end
it 'with login finds searches belonging to user' do
me = FactoryGirl.create(:user)
my_searches = FactoryGirl.create_list(:search, 2, :user => me)
not_me = FactoryGirl.create(:user)
not_my_searches = FactoryGirl.create_list(:search, 2, :user => not_me)
login(me) # want to define this in spec/controllers_helper.rb
get :index
assigns(:searches).should =~ my_searches
end
end
end
Here's the Controller:
# file: app/controllers/searches_controller.rb
class SearchesController < ApplicationController
def index
unless logged_in?
redirect_to login_path, :alert => "You must be logged in to access this page."
else
#searches = Search.where(:user_id => current_user.id)
respond_to do |format|
format.html
format.json { render json: #searches }
end
end
end
end
And here's the ApplicationController code. Note that current_user = x has the effect of logging x in, and it's rather simple: it sets #current_user and session[:user_id].
# file: app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
force_ssl
protected
def current_user
#current_user ||= User.find_by_id(session[:user_id])
end
def current_user=(user)
#current_user = user
session[:user_id] = user && user.id
end
def logged_in?
!!#current_user
end
def require_login
unless logged_in?
redirect_to login_path, :alert => "You must be logged in to access this page."
end
end
helper_method :current_user, :logged_in?, :require_login
end
I may have said this before, but if Stack Overflow gave badges answering one's own questions, I'd have a LOT of badges! :)
Okay, to answer this question you need to look at the documentation for ActionController::TestCase. When you do so, you'll find that it sets up bindings for:
#controller
#request
#response
So for the specific controller given in the OP, writing the login method is trivial:
# file: spec/controllers_helper.rb
module ControllersHelper
def login(user)
#controller.send(:current_user=, user)
end
end
(Did I hear someone say RTFM again? I thought so...)

Need help Rspec test

I'm trying to learn RSpec and writing test for CRUD actions. Here is my controller:
class ArticlesController < ApplicationController
respond_to :html, :json
before_filter :authenticate_user!
# GET /articles
# GET /articles.json
def index
#articles = current_user.articles.all
respond_with(#articles)
end
# GET /articles/1
# GET /articles/1.json
def show
#article = current_user.articles.find(params[:id])
respond_with #article
end
# GET /articles/new
# GET /articles/new.json
def new
#article = current_user.articles.build
respond_with #article
end
# GET /articles/1/edit
def edit
#article = get_article(params[:id])
end
# POST /articles
# POST /articles.json
def create
#article = current_user.articles.build(params[:article])
flash[:notice] = "Article was successfully created!" if #article.save
respond_with(#article, location: articles_path)
end
# PUT /articles/1
# PUT /articles/1.json
def update
#article = get_article(params[:id])
if #article.update_attributes(params[:article])
flash[:notice] = "Article was successfully updated."
end
respond_with #article
end
# DELETE /articles/1
# DELETE /articles/1.json
def destroy
#article = get_article(params[:id])
#article.destroy
respond_with #article
end
private
def get_article(article_id)
current_user.articles.find(article_id)
end
end
And my articles rspec:
describe ArticlesController do
def valid_attributes
{
:title => "Introducting Node.js",
:content => "Node.js is an event-driven...."
}
end
let(:article) do
build(:article, valid_attributes)
end
describe "PUT 'update'" do
before(:each) do
controller.stub_chain(:current_user, :articles, :build) { article }
end
context "success" do
before(:each) do
article.should_receive(:update_attributes).and_return(true)
put :update, id: article.id
end
it "sets notice" do
flash[:notice].should eq("Article was successfully updated!")
end
end
end
describe "POST 'create'" do
before(:each) do
controller.stub_chain(:current_user, :articles, :build) { article }
end
context "success" do
before(:each) do
article.should_receive(:save).and_return(true)
post :create
end
it "sets notice" do
flash[:notice].should eq("Article was successfully created!")
end
it "should redirect to article path" do
response.should redirect_to(articles_path)
end
end
context "failure" do
before(:each) do
article.should_receive(:save).and_return(false).as_null_object
post :create
end
it "assigns #article" do
assigns(:article).should == article
end
end
end
end
My question is when I run rspec on PUT UPDATE test is failed. But POST test is passed. I don't have any idea what is going on. I'm using Rails 3.1.1 with omniauth. I'm not using Devise. Here is the test result. Why? Please help me guys?
Failures:
1) ArticlesController PUT 'update' success sets notice
Failure/Error: put :update, id: article.id
NoMethodError:
undefined method `find' for #<Object:0xa3cfd20>
# ./app/controllers/articles_controller.rb:61:in `get_article'
# ./app/controllers/articles_controller.rb:44:in `update'
# ./spec/controllers/articles_controller_spec.rb:46:in `block (4 levels) in <top (required)>'
Finished in 24.09 seconds
5 examples, 1 failure
Here's the thing.
When you're stubbing, you're just saying "if this method chain is called, return this." There are two issues with that. 1) the code doesn't ever call build, and 2) there's no actual associations.
I believe you'd need to stub current_user.articles to return an article collection. The problem is that AR associations aren't actual arrays, they're proxies.
See this SO post and this SO post for more details. A regular array won't treat the find method like the AR method it really is, and you're not returning a single article.
Since you have the article ID, you could just return that particular article, but your goal is to return that article from within the user's articles to avoid updating someone else's (I assume).
This SO post may also help, and this.
In other words, you may want a real user there, with real associated objects, so things like find will work w/o hackery.
(I fully recognize this isn't a real answer; I've never done this via stubbing, I've used factories/etc.)

rspec controller test with devise authentication

I am having problem with rspec testing controller the devise authentication.
I have a following setup
I have included
config.include Devise::TestHelpers, :type => :controller
in my spec_helper.rb
In my merchants_controller_spec.rb
describe MerchantsController do
before :each do
#user = Factory(:user)
#merchant = Factory(:merchant, :user_id => #user.id,:is_approved => false, :is_blacklisted => false)
controller.stub!(:current_user).and_return(#user)
end
describe "GET index" do
it "assigns all merchants as #merchants" do
merchant = Factory(:merchant,:is_approved => true, :is_blacklisted => false)
get :index
assigns(:merchants).should eq([merchant])
end
end
end
My merchants_controller.rb
class MerchantsController < ApplicationController
before_filter :authenticate_user!
def index
#merchants = Merchant.approved
debugger
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #merchants }
end
end
end
I have a scope approved in merchant model
scope :approved, where(:is_approved => true, :is_blacklisted => false)
Now my problem is even though i stubbed current_user and returned #user as current_user, My merchants_controller index spec is failing. But if i comment out authenticate_user! then the spec passes,
without authenticate_user! the debugger of index action is caught but with authenticate_user! debugger is not caught.
I think there is problem in subbing current_user and i am not able to figure it out.
Help me out..
Have you read through the docs on github?:
Devise includes some tests helpers for functional specs. To use them, you just need to include Devise::TestHelpers in your test class and use the sign_in and sign_out methods. Such methods have the same signature as in controllers:
sign_in :user, #user # sign_in(scope, resource)
sign_in #user # sign_in(resource)
sign_out :user # sign_out(scope)
sign_out #user # sign_out(resource)
Another alternative
RSpec.describe YourController, :type => :controller do
before do
user = FactoryGirl.create(:user)
allow(controller).to receive(:authenticate_user!).and_return(true)
allow(controller).to receive(:current_user).and_return(user)
end
# rest of the code
end

Undefined name method test error in Rails3

Working through Michael Hartl's RailsTutorial and came across the following error - even though I have followed everything to the 'T'.
1) UsersController GET 'index' for signed-in users should have an element for each user
Failure/Error: response.should have_selector("li", :content => user.name)
undefined method `name' for #<Array:0x000001032c07c8>
Did anyone else get a similar error and know how to fix it?
I am in Chapter 10.
Btw, when I try the page it does what it is supposed to do. It's just that the test fails in RSpec.
FYI, here is the related test code from the users_controller_spec.rb
require 'spec_helper'
describe UsersController do
render_views
describe "GET 'index'" do
describe "for non-signed-in users" do
it "should deny access" do
get :index
response.should redirect_to(signin_path)
flash[:notice].should =~ /sign in/i
end
end
describe "for signed-in users" do
before(:each) do
#user = test_sign_in(Factory(:user))
second = Factory(:user, :email => "another#example.com")
third = Factory(:user, :email => "another#example.net")
#users = [#user, second, third]
end
it "should be successful" do
get :index
response.should be_success
end
it "should have the right title" do
get :index
response.should have_selector("title", :content => "All users")
end
it "should have an element for each user" do
get :index
#users.each do |user|
response.should have_selector("li", :content => user.name)
end
end
end
end
My spec/spec_helper.rb file looks like the following:
require 'rubygems'
require 'spork'
Spork.prefork do
# Loading more in this block will cause your tests to run faster. However,
# if you change any configuration or code from libraries loaded here, you'll
# need to restart spork for it take effect.
ENV["RAILS_ENV"] ||= 'test'
unless defined?(Rails)
require File.dirname(__FILE__) + "/../config/environment"
end
require 'rspec/rails'
# Requires supporting files with custom matchers and macros, etc,
# in ./support/ and its subdirectories.
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
Rspec.configure do |config|
# == Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
config.mock_with :rspec
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, comment the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
### Part of a Spork hack. See http://bit.ly/arY19y
# Emulate initializer set_clear_dependencies_hook in
# railties/lib/rails/application/bootstrap.rb
ActiveSupport::Dependencies.clear
def test_sign_in(user)
controller.sign_in(user)
end
def integration_sign_in(user)
visit signin_path
fill_in :email, :with => user.email
fill_in :password, :with => user.password
click_button
end
end
end
Spork.each_run do
end
it appears your test_sign_in method is returning an instance of an array rather than a User object. Are you explicitly returning a user object in the test_sign_in method? If not, have a look at the last line that's executed in that method, I have a feeling the result of it is an array.
I solved this issue, and the answer can be found on the railstutorial official forums.