FactoryGirl.define do
factory :card do
card_no '6217920016000864'
factory :invalid_card do
card_no nil
end
end
end
card_controller_spec.rb
describe CardsController do
describe 'GET #index' do
it 'assigns card' do
card = create(:card)
get :index
expect(assigns(:card)).to eq([card])
end
it 'show index' do
expect(response).to render_template("index")
end
end
end
cards_controller.rb
class CardsController < ApplicationController
def index
if current_user.login_name=="admin"
#admin
#cardlist = set_paginate Card
else
#普通管理员
#restaurant_ids = Restaurant.where('parent_id = ? or id = ?', current_user.restaurant.id, current_user.restaurant.id).collect { |r| r.id }
#cardlist = set_paginate Card.where('restaurant_id in (?) ', #restaurant_ids)
end
end
end
two errors like this expecting <"index"> but rendering with <""> and expect(assigns(:card)).to eq([card]) got nil.
help me thanks!
First test is for what data controller provides to the view. Looking on the controller you have you should check for assigns(:cartdlist) I think. And use match_array rather than eq.
Second test doesn't render anything as it doesn't go anywhere (your get is just for the first it block). So try this:
describe CardsController do
describe 'GET #index' do
before do
card = create(:card)
get :index
end
it 'assigns card' do
expect(assigns(:cardlist)).to match_array([card])
end
it 'show index' do
expect(response).to render_template("index")
end
end
end
It's probably not the problem in your case as you got different error but to allow expect syntax your spec_helper.rb file should contain:
RSpec.configure do |config|
# most omitted ...
config.expect_with :rspec do |c|
c.syntax = :expect
end
end
or
c.syntax = [:should, :expect]
for both
This
expect(assigns(:card)).to eq([card]) got nil
because I believe you assign data to cards (after updating question: cardlist)
This
expecting <"index"> but rendering with <"">
because you don't call get :index in test
Related
I'm stuck on a test scenario.
I have a controller method:
def update
#object = Object.find params[:id]
# some other stuff
#object.save
rescue ActiveRecord::StaleObjectError
# woo other stuff
end
The first part I test with:
context '#update'
let(:object) { double }
it 'nothing fails' do
# some other stuff
expect(Object).to receive(:find).with('5').and_return(object)
expect(object).to receive(:save).and_return(true)
xhr :put, :update, id:5
expect(response).to be_success
expect(assigns(:object)).to eq(object)
end
end
Now I want to test the ActiveRecord::StaleObjectError exception. I want to stub it, but I didn't find any solution how to do this.
So my question is, how to raise the ActiveRecord::StaleObjectError in an RSpec test?
Like this, for example
expect(object).to receive(:save).and_raise(ActiveRecord::StaleObjectError)
I would do something like this:
describe '#update' do
let(:object) { double }
before do
allow(Object).to receive(:find).with('5').and_return(object)
xhr(:put, :update, id: 5)
end
context 'when `save` is successful' do
before do
allow(object).to receive(:save).and_return(true)
end
it 'returns the object' do
expect(response).to be_success
expect(assigns(:object)).to eq(object)
end
end
context 'when `save` raises a `StaleObjectError`' do
before do
allow(object).to receive(:save).and_raise(ActiveRecord::StaleObjectError)
end
it 'is not successful' do
expect(response).to_not be_success
end
end
end
Please note that I make a difference between stubing methods in the test setup (I prefer allow in this case) and the actual expectation (expect).
Okay,
this is driving me nuts, since I don't understand the error in this case.
I have the following class defined:
module Admins
class BasePresenter < ::BasePresenter
def render_customer(id:)
return I18n.t('admin.admin') if id.nil?
::Customer.where(id: id).first.try(:name) || I18n.t('admin.deleted')
end
def percent_of(count, total)
((count.to_f / total.to_f) * 100.0).to_i
end
end
end
Which inherits from the BasePresenter below:
class BasePresenter
def initialize(object, template)
#object = object
#template = template
end
def self.presents(name)
define_method(name) do
#object
end
end
def underscored_class
#object.class.name.underscore
end
protected
def h
#template
end
def handle_none(value, html = true)
if value.present?
if block_given?
yield
else
value
end
else
return h.content_tag(:span, '-', class: 'none') if html
'-'
end
end
def current_customer
#current_customer ||= h.current_customer
end
def current_user
#current_user ||= h.current_user
end
end
However when I try to run my specs, I receive the following error from RSpec:
ArgumentError: wrong number of arguments (0 for 2)
./app/presenters/base_presenter.rb:3:in initialize'
./spec/presenters/admins/base_presenter_spec.rb:24:inblock (3
levels) in '
The class is no different from other presents, where the the inheritance works in the exact same way and those tests are passing.
Just the test for this class is failing with this error, and only when testing the method percent_of.
What am I failing to see?
EDIT
This is my RSpec test:
require 'spec_helper'
describe ::Admins::BasePresenter do
describe '#render_customer' do
let(:customer) { Customer.first }
subject { ::Admins::BasePresenter.new(Object.new, ApplicationController.new.view_context) }
it 'returns the I18n translations for (admin) when no customer is set.' do
expect(subject.render_customer(id: nil)).to eql(I18n.t('admin.admin'))
end
it 'returns the proper name when a valid ID is given' do
expect(subject.render_customer(id: customer.id)).to eql(customer.name)
end
it 'returns the I18n translations for (deleted) when an invalid ID is given' do
expect(subject.render_customer(id: -1)).to eql(I18n.t('admin.deleted'))
end
end
describe '#percent_of' do
it 'calculates the percentage correctly' do
expect(subject.percent_of(0, 1)).to eql(0)
expect(subject.percent_of(1, 1)).to eql(100)
expect(subject.percent_of(1, 2)).to eql(50)
expect(subject.percent_of(1, 3)).to eql(33)
end
end
end
Ugh,
I'm an idiot....
The problem was that my subject was defined inside a Describe block for specific tests and the second one did not have any.
Which means our hooks try to create an instance of the class in the outer describe block...
This was the fix:
require 'spec_helper'
describe ::Admins::BasePresenter do
let(:customer) { Customer.first }
subject { ::Admins::BasePresenter.new(Object.new, ApplicationController.new.view_context) }
describe '#render_customer' do
it 'returns the I18n translations for (admin) when no customer is set.' do
expect(subject.render_customer(id: nil)).to eql(I18n.t('admin.admin'))
end
it 'returns the proper name when a valid ID is given' do
expect(subject.render_customer(id: customer.id)).to eql(customer.name)
end
it 'returns the I18n translations for (deleted) when an invalid ID is given' do
expect(subject.render_customer(id: -1)).to eql(I18n.t('admin.deleted'))
end
end
describe '#percent_of' do
it 'calculates the percentage correctly' do
expect(subject.percent_of(0, 1)).to eql(0)
expect(subject.percent_of(1, 1)).to eql(100)
expect(subject.percent_of(1, 2)).to eql(50)
expect(subject.percent_of(1, 3)).to eql(33)
end
end
end
I want to test the controller method, but I can not find the example of testing method with order and search .
This is my controller:
class Admin::HotelsController < Admin::BaseController
helper_method :sort_column, :sort_direction
def index
#hotels = Hotel.search(params[:search], params[:search_column]).order(sort_column + ' ' + sort_direction)
end
def show
#hotel = Hotel.find(params[:id])
end
def update
#hotel = Hotel.find(params[:id])
if #hotel.update_attributes(hotel_params)
redirect_to admin_hotels_path
else
render(:edit)
end
end
private
def hotel_params
params.require(:hotel).permit(:title, :description, :user_id, :avatar, :price, :breakfast, :status, address_attributes: [:state, :country, :city, :street])
end
def sort_column
Hotel.column_names.include?(params[:sort]) ? params[:sort] : 'created_at'
end
def sort_direction
%w[asc desc].include?(params[:direction]) ? params[:direction] : 'asc'
end
end
This is test for this controller.
require 'rails_helper'
describe Admin::HotelsController do
login_admin
describe 'GET index' do
it 'render a list of hotels' do
hotel1, hotel2 = create(:hotel), create(:hotel)
get :index
expect(assigns(:hotels)).to match_array([hotel1, hotel2])
end
end
describe 'GET show' do
it 'should show hotel' do
#hotel = create(:hotel)
get :show, { id: #hotel.to_param, template: 'hotels/show' }
expect(response).to render_template :show
end
end
end
I don't know how testing index method. Please help or give me a link with information about this. Thanks!
If it may help you, I personally prefer to have minimals tests for the controllers for various reasons:
1) as I was beginning in rails testing I read many articles saying it's a good idea
2) it allows you to tests in isolation model methods:
describe 'GET index' do
it 'render a list of hotels' do
hotel1, hotel2 = create(:hotel), create(:hotel)
get :index
expect(assigns(:hotels)).to match_array([hotel1, hotel2])
end
end
here your test matches the result of your query on the model. You can split it like this:
describe 'GET index' do
it 'render a list of hotels' do
hotel1, hotel2 = create(:hotel), create(:hotel)
Hotel.should_receive(:search).with(YOUR PARAMS)
get :index
response.response_code.should == 200
end
end
and then test the result of Hotel.search in a model test.
3) it allows you to test the feature and not some random things that are not really relevant:
describe 'GET show' do
it 'should show hotel' do
#hotel = create(:hotel)
get :show, { id: #hotel.to_param, template: 'hotels/show' }
expect(response).to render_template :show
end
end
here "expect(response).to render_template :show" seems like testing that rails rendering system is properly working. I assume that's not what you want to test, you may prefer (that's what I would do):
describe 'GET show' do
it 'should show hotel' do
#hotel = create(:hotel)
Hotel.should_receive(:find).with(YOUR PARAMS)
get :show, { id: #hotel.to_param, template: 'hotels/show' }
response.response_code.should == 200
end
end
and then test what is supposed to appear on the web page with a feature test using something like capybara gem unless you're rendering some json: in this case match the json values in the controller.
By the way: "#hotel = create(:hotel)" the # is not necessary here as you're in the "it". Moreover you can create such entry like this:
context "" do
before(:each) do
#hotel = create(:hotel) # here the # is necessary for the variable to be
end # accessible in the it
it "" do
end
end
or even like this:
context "" do
let(:hotel) { create(:hotel) } # you can call it in the test by using hotel and it
it "" do # will be insert in you db only when it's in the "it"
end # if you want it to be created in the "it" without
end # calling hotel for nothing, use let!
I would suggest using
describe 'GET index' do
let(:hotel1) { create(:hotel) }
let(:hotel2) { create(:hotel) }
it 'render index template' do
get :index
expect(response).to render_template :index
end
it 'render asc ordered hotels' do
get :index
# if you are using json responses
json = JSON.parse(response.body)
expect(json['hotels'].first).to eq hotel1
expect(json['hotels'].last ).to eq hotel2
# or any similar approach to get test the hotels in response
end
it 'render desc ordered hotels' do
get :index, {direction: 'desc'}
# if you are using json responses
json = JSON.parse(response.body)
expect(json['hotels'].first).to eq hotel2
expect(json['hotels'].last ).to eq hotel1
# or any similar approach to get test the hotels in response
end
# you can complete these tests yourself
it 'render hotels sorted with different_column_than_created_at asc'
it 'render hotels sorted with different_column_than_created_at desc'
end
I wrote a spec to test the instance variable #vendors in the index action of my vendors controller. If I remove #vendors from the vendors controller the spec still passes. Any ideas as to why assigns(:vendors) would pass if #vendors doesn't exists in the controller. Heres my code:
Vendors Controller
class VendorsController < ApplicationController
load_and_authorize_resource
def index
# #vendors = Vendor.all
end
end
Vendors Controller Spec
require 'spec_helper'
require 'ruby-debug'
describe VendorsController do
login_user
before(:each) do
#vendor = Factory(:vendor)
end
describe "GET index" do
before(:each) do
#ability.can :read, Vendor
end
it "assigns all vendors to #vendors" do
get :index
assigns(:vendors).should == [#vendor]
end
it "should render the index template" do
get :index
response.should be_success
response.code.should eq("200")
response.should render_template("index")
end
end
end
Vendors Factory
FactoryGirl.define do
factory :vendor do |f|
f.sequence(:name) { |n| "Test#{n}" }
f.sequence(:address) { |n| "000 Test#{n} drive, Hampton" }
f.state "Virginia"
f.zip "00000"
f.sequence(:telephone) { |n| "000-000-000#{n}" }
f.sequence(:poc) { |n| "Test#{n}" }
end
end
Thanks
Because load_and_authorize_resource actually loads and authorizes.
So your code is unnecessary.
You could change with authorize_resource, thus the spec will fail.
I was wondering if i could have some feedbacks with the controller spec bellow. In fact i'm new when writing specs and controller's spec are way different from model's spec ! So i'm wondering if i may not go in the wrong direction...
subjects_controller.rb
def show
#subject = Subject.find(params[:id])
if #subject.trusted?(current_user)
#messages = #subject.messages
else
#messages = #subject.messages.public
#messages = #messages + #subject.messages.where(:user_ids => current_user.id)
#messages.uniq!
end
# sort the list
#messages = #messages.sort_by(&:created_at).reverse
if !#subject.company.id == current_user.company.id
redirect_to(subjects_path, :notice => "Invalid subject")
end
end
subjects_controller_spec.rb
require 'spec_helper'
describe SubjectsController do
before(:each) do
#subject = mock_model(Subject)
end
context "for signed users" do
before(:each) do
#current_user = sign_in Factory(:user)
end
context "GET #show" do
before(:each) do
Subject.stub!(:find, #subject).and_return(#subject)
end
context "when current_user is trusted" do
before(:each) do
messages = []
company = mock_model(Company)
#subject.should_receive(:trusted?).and_return(true)
#subject.should_receive(:messages).and_return(messages)
#subject.should_receive(:company).and_return(company)
end
it "should render success" do
get :show, :id => #subject
response.should be_success
end
end
context "when current_user is not trusted" do
before(:each) do
messages = []
company = mock_model(Company)
#subject.should_receive(:trusted?).and_return(false)
#subject.should_receive(:messages).and_return(messages)
messages.should_receive(:public).and_return(messages)
#subject.should_receive(:messages).and_return(messages)
messages.should_receive(:where).and_return(messages)
#subject.should_receive(:company).and_return(company)
end
it "should render success" do
get :show, :id => #subject
response.should be_success
end
end
context "when subject's company is not equal to current_user's company" do
# I have no idea of how to implement ==
end
end
end
end
Factories.rb
Factory.define :user do |u|
u.first_name 'Test User' #
u.username 'Test User' #
u.surname 'TheTest' #
u.email 'foo#foobar.com' #
u.password 'please' #
u.confirmed_at Time.now #
end
As far as I can tell you're on the right path. The basic idea is to completely isolate your controller code from model and view in these tests. You appear to be doing that--stubbing and mocking model interaction.
Don't write RSpec controller specs at all. Use Cucumber stories instead. Much easier, and you get better coverage.