I have a services model defined as such:
class Service < ActiveRecord::Base
attr_accessible :service_type #, ...
SERVICE_TYPES = {
:restaurant => "restaurant",
:retailer => "retailer"
}
SERVICE_TYPES.values.each do |method|
define_method method.pluralize.to_sym do
where(:service_type => method)
end
end
def domestic
where(:country => 'USA')
end
def international
where("country != 'USA'")
end
end
I decided to not use a traditional STI pattern for the different services because they will all have the same attributes and otherwise behave identically. There is a parent model, but it is used only for normalization purposes and the web user never needs to know about it. As far as the application goes, there are service types, and specific services under those. So basically I want URLs like this:
http://myapp/services # <- Lists all service types
http://myapp/restaurants # <- Lists all restaurant-type services
http://myapp/restaurants/foo # <- Lists a specific restaurant (using friendly_id gem)
http://myapp/retailers # <- Lists all retailer-type services
http://myapp/retailers/bar # <- Lists a specific retailer
So I created a routing pattern like so:
controller :services, :via => [:get] do
match '/services' => :index
match '/:service_type' => :service_index, :as => 'service_type', :constraints => { :service_type => Regexp.new(Service::SERVICE_TYPES.values.map{|s| Regexp.escape(s.pluralize) }.join('|')) }
match '/:service_type/:id' => :show, :as => 'service', :constraints => { :service_type => Regexp.new(Service::SERVICE_TYPES.values.map{|s| Regexp.escape(s.pluralize) }.join('|')) }
end
Which gets me
services GET /services(.:format) {:controller=>"services", :action=>"index"}
service_type GET /:service_type(.:format) {:service_type=>/restaurants|retailers/, :controller=>"services", :action=>"service_index"}
service GET /:service_type/:id(.:format) {:service_type=>/restaurants|retailers/, :controller=>"services", :action=>"show"}
And the controller is defined as
class ServicesController < ApplicationController
def index
#service_types = Service::SERVICE_TYPES.values
#page_title = "Services"
end
def service_index
#service_type = params[:service_type]
#domestic = Service.send(#service_type).domestic
#international = Service.send(#service_type).international
#page_title = #service_type.capitalize
end
def show
#service = Service.find(params[:id])
#page_title = "Services - #{#service.name}"
end
end
This all works as expected when I test it in the browser. But when I try to run an integration test in Rspec2 I'm getting some really unexpected behavior.
require 'spec_helper'
describe ServicesController do
describe "GET 'index'" do
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 => "#{#base_title}Services"
)
end
it "should have a link to each service" do
get :index
Service::SERVICE_TYPES.values.each do |service_type|
response.should have_selector("a", :href => service_type_path(service_type.pluralize),
:content => service_type.pluralize)
end
end
end
describe "GET 'service_index'" do
Service::SERVICE_TYPES.values.each do |service_type|
context service_type.pluralize do
it "should be successful" do
get :service_index, :service_type => service_type.pluralize
response.should be_success
end
it "should have the right title" do
get :service_index, :service_type => service_type.pluralize
response.should have_selector(
"title",
:content => "#{#base_title}#{service_type.pluralize.capitalize}"
)
end
it "should have a link to each service" do
get :service_index, :service_type => service_type.pluralize
Service.send(service_type.pluralize).each do |service|
response.should have_selector("a", :href => service_path(service_type.pluralize, service),
:content => service.name)
end
end
end
end
end
describe "GET 'show'" do
it "should be successful" do
get 'show'
response.should be_success
end
end
end
The get action appears to run successfully because the "should be successful" tests all pass, but the others fail because it can't find the proper selector(s) on the page. The odd thing is that the dump of HTML that is returned doesn't appear to come from my app.
1) ServicesController GET 'index' should have the right title
Failure/Error: response.should have_selector(
expected following output to contain a <title>Services</title> tag:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# ./spec/controllers/services_controller_spec.rb:12:in `block (3 levels) in <top (required)>'
2) ServicesController GET 'index' should have a link to each service
Failure/Error: response.should have_selector("a", :href => service_type_path(service_type.pluralize),
expected following output to contain a <a href='/restaurants'>Restaurants</a> tag:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# ./spec/controllers/services_controller_spec.rb:21:in `block (4 levels) in <top (required)>'
# ./spec/controllers/services_controller_spec.rb:20:in `each'
# ./spec/controllers/services_controller_spec.rb:20:in `block (3 levels) in <top (required)>'
3) ServicesController GET 'service_index' restaurants should have the right title
Failure/Error: response.should have_selector(
expected following output to contain a <title>Restaurants</title> tag:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# ./spec/controllers/services_controller_spec.rb:37:in `block (5 levels) in <top (required)>'
4) ServicesController GET 'service_index' restaurants should have a link to each service
Failure/Error: response.should have_selector("a", :href => service_path(service_type.pluralize, service),
expected following output to contain a <a href='/restaurants/foo'>Foo</a> tag:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# ./spec/controllers/services_controller_spec.rb:46:in `block (6 levels) in <top (required)>'
# ./spec/controllers/services_controller_spec.rb:45:in `block (5 levels) in <top (required)>'
What I would expect to see if this was indeed a failure would be the complete dump of my HTML application template which is marked up in HTML 5. Instead I'm getting only the doctype of a page marked up as HTML 4.
Any ideas what would cause that?
As a secondary issue, I can't figure out any way to turn on logging in Webrat to see what HTTP activity is actually happening. How can I do that?
Yeah, never mind. I'm an idiot and forgot to put render_views inside my test which is why it wasn't rendering the view.
Related
To start, in my request spec sites.spec.rb I have this test:
describe "POST /v1/sites" do
let(:valid_attributes) { { url: "www.example.com", site_code: "123456" } }
context 'when the request is valid' do
before { post v1_sites_path, params: valid_attributes }
it 'creates a site' do
expect(json['url']).to eq("www.example.com")
expect(json['site_code']).to eq("123456")
end
it 'returns status code 201' do
expect(response).to have_http_status(201)
end
end
I then get a failing test for "creates a site"...
1) Sites POST /v1/sites when the request is valid creates a site
Failure/Error: expect(json['url']).to eq("www.example.com")
expected: "www.example.com"
got: ["can't be blank"]
(compared using ==)
# ./spec/requests/sites_spec.rb:61:in `block (4 levels) in <top (required)>'
# ./spec/rails_helper.rb:84:in `block (3 levels) in <top (required)>'
# ./spec/rails_helper.rb:83:in `block (2 levels) in <top (required)>'
Now this technically makes sense because my Site model has validates :url, :site_code, presence: true. So the test is failing because the post is not passing the params correctly.
Lastly, here is the controller:
module Api::V1
class SitesController < BaseApiController
before_action :set_site, only: [:show, :update, :destroy]
# GET /sites
def index
#sites = Site.all
render json: #sites
end
# GET /sites/1
def show
render json: #site
end
# POST /sites
def create
#site = Site.new(site_params)
if #site.save
render json: #site, status: :created, location: #site
else
render json: #site.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /sites/1
def update
if #site.update(site_params)
render json: #site
else
render json: #site.errors, status: :unprocessable_entity
end
end
# DELETE /sites/1
def destroy
#site.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_site
#site = Site.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def site_params
# params.require(:data).require(:attributes).permit(:url, :side_code, :user_id)
# params.require(:site).permit(:url, :side_code, :user_id)
params.fetch(:site, {}).permit(:url, :side_code)
end
end
end
I am speculating that the way I am passing parameters to the post for Rails API is perhaps not formatted or correctly or something else entirely. I did play with the params in the test block trying data: { attributes: valid_attributes } with no luck.
Any thoughts or suggestions are greatly appreciated!
This problem was indeed due to format of the parameters I was passing to POST request in the test block. I tested the POST via the command line and watched the rails server to see how the params were coming through. They looked like this:
Parameters: {"site_code"=>"123456", "url"=>"www.updated.com", "subdomain"=>"api", "id"=>"2", "site"=>{"site_code"=>"123456", "url"=>"www.updated.com"}}
Then in my sites_spec.rb, I copied this format for the valid params of the post request:
let(:valid_attributes) { { "site"=>{"url"=>"www.example.com", "user_id"=>user_id, "site_code"=>"123456"} } }
This works. The JSON format of the params needed to be formatted in the test block the same way they would be if it was real JSON request.
I have configured Rspec Ruby 2.0.0-p0 and Rails 3.2.14 configuration is perfect I'm sure on that but when I try to run rake spec:controllers it gaves me below error on every request action written in spec example -
*** NoMethodError Exception: undefined method `to_sym' for nil:NilClass
I have written specs for controllers before but never come across such situation, help me If any one has fixed same issue....
Here is my spec and error stack
describe UsersController do
before (:each) do
#user = FactoryGirl.create(:user)
sign_in #user
end
describe "GET 'index'" do
it "should be successful" do
get 'index'
response.should be_success
end
end
describe "GET 'show'" do
it "should be successful" do
get :show, :id => #user.id
response.should be_success
end
it "should find the right user" do
get :show, :id => #user.id
assigns(:user).should == #user
end
end
end
Here is result -
Failures:
1) UsersController GET 'index' should be successful
Failure/Error: get 'index'
NoMethodError:
undefined method `to_sym' for nil:NilClass
# ./spec/controllers/users_controller_spec.rb:13:in `block (3 levels) in <top (required)>'
2) UsersController GET 'show' should be successful
Failure/Error: get :show, :id => #user.id
NoMethodError:
undefined method `to_sym' for nil:NilClass
# ./spec/controllers/users_controller_spec.rb:21:in `block (3 levels) in <top (required)>'
3) UsersController GET 'show' should find the right user
Failure/Error: get :show, :id => #user.id
NoMethodError:
undefined method `to_sym' for nil:NilClass
# ./spec/controllers/users_controller_spec.rb:26:in `block (3 levels) in <top (required)>'
Finished in 0.4741 seconds
3 examples, 3 failures
Failed examples:
rspec ./spec/controllers/users_controller_spec.rb:12 # UsersController GET 'index' should be successful
rspec ./spec/controllers/users_controller_spec.rb:20 # UsersController GET 'show' should be successful
rspec ./spec/controllers/users_controller_spec.rb:25 # UsersController GET 'show' should find the right user
Randomized with seed 19701
My factory is -
FactoryGirl.define do
factory :user do
first_name 'Test User'
last_name 'Last name'
email 'example#example.com'
password 'changeme'
password_confirmation 'changeme'
company 'RR'
confirmed_at Time.now
end
end
We need company name mandatory to create register user.
class UsersController < ApplicationController
before_filter :authenticate_user!
#authorize_resource
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
end
here I have added two methods for test in user controller.
Same thing I tried with demo example that works great but not in my project....
Thanks
If you are using devise gem for authentication then you need to specify the devise mapping in controller test cases.
Replace the code in the before(:each) block with below code
#user = FactoryGirl.create(:user)
#request.env['devise.mapping'] = Devise.mappings[:user]
sign_in #user
Finally I fixed issue, it comes due to authenticate_user! method in application controller which inherits to every controllers -
prepend_before_filter :authenticate_user!, :except => [:not_authenticated]
I have added below module to fix issue occurs due to this devise method -(support/controllers_helpers.rb)
module ControllerHelpers
def sign_in(user = double('user'))
if user.nil?
request.env['warden'].stub(:authenticate!).
and_throw(:warden, {:scope => :user})
controller.stub :current_user => nil
else
request.env['warden'].stub :authenticate! => user
controller.stub :current_user => user
end
end
end
finally include above module in spec_helper.rb file
config.include ControllerHelpers, :type => :controller
Got running all controllers specs.
Cheers!!!
more info
How to pass id. my controller is:
class AttachementsController < ApplicationController
def index
#pdf = Attachement.find(params[:resume_id])
# send_file(#pdf.file.url, :type => 'application/pdf', :disposition => 'inline',:stream => false)
redirect_to #pdf.file.url
end
end
and my test case of the controller is:
require 'spec_helper'
describe AttachementsController do
describe "GET 'index'" do
it "should be successful" do
get 'index', :id => "#attachements.id"
response.should be_success
end
end
end
and my error is:
AttachementsController GET 'index' should be successful
Failure/Error: get 'index', :id => "#attachements.id"
ActiveRecord::RecordNotFound:
Couldn't find Attachement without an ID
# ./app/controllers/attachements_controller.rb:3:in `index'
# ./spec/controllers/attachements_controller_spec.rb:7:in `block (3 levels) in <top (required)>'
You don't need the quotes around #attachements.id.
get 'index', :id => #attachements.id
I need some help guys, trying to make this test to pass but with no luck.
describe 'PUT posts/:id' do
describe 'with valid attributes' do
let(:mock_post) { mock_model('Post', title: 'hey! iam a mock!', description: 'a sexy model', location: 'everywhere') }
login_user
it 'should update the object and redirect to the post' do
Post.stub!(:find).with(mock_post.id).and_return(mock_post)
Post.any_instance.should_receive(:update_attributes).with({"these" => "params"}).and_return(true)
response.should redirect_to post_path(mock_post)
put :update, id: mock_post.id, post: { these: 'params' }
end
it 'should have a current_user' do
subject.current_user.should_not be_nil
end
end
For now, I have something like the above test and getting the following error:
1) PostsController PUT posts/:id with valid attributes should update the object and redirect to the post
Failure/Error: response.should redirect_to post_path(mock_post)
Expected response to be a <:redirect>, but was <200>
# ./spec/controllers/posts_controller_spec.rb:200:in `block (4 levels) in <top (required)>'
PostsController:
class PostsController < ApplicationController
load_and_authorize_resource except: [:index, :show]
before_filter :authenticate_user!, except: [:index, :show, :tags]
before_filter :find_post, only: [:show, :edit, :update, :suspend, :suspend_alert]
def update
if #post.update_attributes(params[:post])
flash[:success] = 'Cool.'
redirect_to post_path(#post)
else
render :edit
end
end
protected
def find_post
#post = Post.find(params[:id])
end
end
Also, how should I write the test for the render :edit part?
Your spec never calls the controller action. Try adding:
Post.any_instance.
should_receive(:update_attributes).
with({"these" => "params"})
put :update, :id => "1", :post => {"these" => "params"}
To test the two paths that result from the call to update_attributes, substitute the value in the expectation:
it "should redirect when successful" do
Post.any_instance.
should_receive(:update_attributes).
with({"these" => "params"}).
and_return(true)`
response.should_redirect_to(post_path(#mock_post))
put :update, :id => "1", :post => {"these" => "params"}
end
it "should render the edit page when unsuccessful" do
Post.any_instance.
should_receive(:update_attributes).
with({"these" => "params"}).
and_return(false)`
response.should render_template("edit")
put :update, :id => "1", :post => {"these" => "params"}
end
I am seeing an error that is only dependent on the location of the line:
should change(Relationship, :count).by(-1)
For example, with the code:
it "should destroy a relationship using Ajax" do
lambda do
xhr :delete, :destroy, :id => #relationship
response.should be_success
end.should change(Relationship, :count).by(-1) #<<-line is here
I get the rspec error:
1) RelationshipsController DELETE 'destroy' should destroy a relationship using Ajax
Failure/Error: xhr :delete, :destroy, :id => #relationship
ActionView::MissingTemplate:
Missing template relationships/destroy with {:handlers=>[:erb, :rjs, :builder, :rhtml, :rxml], :formats=>[:js, :html], :locale=>[:en, :en]} in view paths "#<RSpec::Rails::ViewRendering::PathSetDelegatorResolver:0x00000100a5b5f8>"
# ./app/controllers/relationships_controller.rb:16:in `destroy'
# ./spec/controllers/relationships_controller_spec.rb:44:in `block (4 levels) in <top (required)>'
# ./spec/controllers/relationships_controller_spec.rb:43:in `block (3 levels) in <top (required)>'
But with the code:
it "should destroy a relationship using Ajax" do
lambda do
xhr :delete, :destroy, :id => #relationship
response.should be_success
should change(Relationship, :count).by(-1) #<<-Line moved to here
end
... the test passes.
I am seeing expected behavior when I use a web browser. Unfollowing a user adjusts totals correctly.
So, are these two rspec tests not equivalent? Am I falsely reassured that the second test passes? If they are functionally equivalent, why does the first one fail?
Seems this was due, in part, to having confused
./app/controllers/relationships_controller.rb
with
./spec/controllers/relationships_controller_spec.rb
Code pasted into the wrong file (appropriately) generated no syntax errors. Fixing my error lead to passing rspec tests.