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
Related
Hi I Try to create a mock for follow class:
module EstablishmentsQueryService
class << self
def find_by_id(id)
Establishment.find_by!(id:)
rescue ActiveRecord::RecordNotFound
raise EstablishmentNotFoundError.new id
end
end
end
to try test my controller
# frozen_string_literal: true
module Api
module V1
# Controllewr to manager Establishments
class EstablishmentsController < Api::V1::ApiController
before_action :validate_id, only: %i[destroy update show]
before_action :load_establishment, only: %i[destroy update show]
def show; end
def create
#establishment = Establishment.new(establishment_params)
#establishment = EstablishmentService.save(#establishment)
render status: :created
end
def destroy
EstablishmentService.delete(#establishment)
end
def update
#establishment.attributes = establishment_params
#establishment = EstablishmentService.save(#establishment)
end
private
def validate_id
message = I18n.t('establishment_controller.id.invalid', id: params[:id])
UuidValidateService.call(params[:id], message)
end
def load_establishment
#establishment = EstablishmentsQueryService.find_by_id(params[:id])
end
def establishment_params
params.require(:establishment).permit(:name, :cnpj, :description)
end
end
end
end
follow my test:
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Api::V1::Establishments', type: :request do
describe 'GET /api/v1/establishments/:id' do
context 'when has establishment' do
let(:establishment) { build(:establishment, id: p, created_at: DateTime.now, updated_at: DateTime.now) }
before do
allow_any_instance_of(EstablishmentsQueryService).to receive(:find_by_id).and_return(establishment)
get "/api/v1/establishments/#{establishment.id}"
end
it 'then http status is ok' do
expect_status_is_ok
end
it 'has body equal to expected' do
except_field_by_field(establishment, body_to_open_struct, %i[id name cnpj description])
end
end
context 'when has no establishment' do
before do
get "/api/v1/establishments/#{UUID.new.generate}"
end
it 'then http status is not_found' do
expect_status_is_not_found
end
end
context 'when use invalid id' do
before { get "/api/v1/establishments/#{FFaker::Lorem.word}" }
it 'then http status is bad_request' do
expect_status_is_bad_request
end
end
end
describe 'PUT /api/v1/establishments/:id' do
let(:establishments_query_service) { allow(EstablishmentsQueryService) }
let(:establishments_service) { allow(EstablishmentsService) }
context 'when updated with success' do
let(:establishment) { build(:establishment) }
let(:id) { UUID.new.generate }
before do
establishments_query_service.to receive(:find_by_id) { |p| build(:establishment, id: p, created_at: DateTime.now, updated_at: DateTime.now) }
establishments_service.to receive(:save) do |p|
to_return = p
to_return.created_at = DateTime.now
to_return.updated_at = DateTime.now
end
put "/api/v1/establishments/#{id}"
end
it 'then http status is ok' do
expect_status_is_ok
end
it 'has body equal to expected' do
actual = body_to_open_struct
except_field_by_field(establishment, actual, %i[name cnpj description])
expected(actual.id).to eq(id)
end
end
context 'when has no establishment' do
end
context 'when has constraint violation' do
end
end
describe 'DELETE /api/v1/establishments/:id' do
end
describe 'POST /api/v1/establishments' do
end
end
If I work using allow_any_instance_of a test ignore configuration, use a real configuration and fails because has no data stores. If I use double I received a follow error:
Api::V1::Establishments GET /api/v1/establishments/:id when has establishment then http status is ok
Failure/Error: allow_any_instance_of(EstablishmentsQueryService).to receive(:find_by_id).and_return(establishment)
EstablishmentsQueryService does not implement #find_by_id
I think the right away is user allow_any_instance_of because this config is for static methods, but didn't work
how can I mock my class to test my controller? I using Ruby 3.1.2, rails 7.0.3 and rspec-rails 5.1.2
thank you
I found my problem, I forgot to definie expected params in my confi using with()
allow(EstablishmentsQueryService).to receive(:find_by_id).with(establishment.id).and_return(establishment)
i am new about rails and respect, so i hope found the answer here,
i have some models here, and all of it have many to many relations
user -- role_user -- role -- role_feature -- feature
role_user and role_feature is the middle table to connect the many to many relations, I have completed all the controller crud and the model relations table, and also tested both of it with rspec and it is 100% coverage, so dont worry about it. and i haven't do anything about views, just model, controller and rspect.
Now I want to create privilege/permissions for each user in accessing existing features, so each user has a role and the role has many features,
in the file seeds.rb
RoleUser.where(role_id: 1, user_id:1).first_or_create;
Role.where(holding_company_id: 1, name: 'Area Index').first_or_create;
RoleFeature.where(role_id: 1, feature_id:1).first_or_create;
Feature.where(name: 'Area Index', key: 'area_index').first_or_create;
Feature.where(name: 'Area Create', key: 'area_create').first_or_create;
in the file area.rb (i take example area)
class AreasController < ApplicationController
before_filter :check_access
def index
if params[:per_page].blank? && params[:page].blank?
areas = Area.where(holding_company_id: current_holding_company.id)
else
areas = Area.where(holding_company_id: current_holding_company.id).paginate(page: params[:page], per_page: params[:per_page])
end
respond_to do |format|
format.json { render json: areas.to_json, status: 200 }
end
end
def create
end
def update
end
def show
end
def destroy
end
protected
def check_access
case params[:action]
when 'index'
unless current_user.roles.features.include? (area_index)
render nothing: true, status: 200
end
end
end
end
and the spec is
require 'spec_helper'
describe AreasController, :type => :controller do
before(:each) do
#token = FactoryGirl.create(:token)
#area = FactoryGirl.create(:area_1)
FactoryGirl.create(:area_2)
FactoryGirl.create(:area_3)
end
describe "GET 'index'" do
it "returns http success for json without pagination" do
get 'index', token: #token.token_string, format: :json
expect(response.status).to eq(200)
expect(JSON.parse(response.body).count).to eq(3)
expect(Area.count).to eq(3)
end
it "returns http success for json with pagination" do
get 'index', token: #token.token_string, format: :json, page: 1, per_page: 2
expect(response.status).to eq(200)
expect(JSON.parse(response.body).count).to eq(2)
end
end
describe "POST 'create'" do
end
describe "PUT 'update'" do
end
describe "GET 'show'" do
end
describe "DELETE 'destroy'" do
end
end
and the problem is there is an error/failure while testing it with rspec, its said undefined method role, i try to fix it , i spent hours just for browse it on internet, but it's useless. so i hope this is the final chance for me, i need your help guys...
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
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.