# Gemfile
group :test do
#...
gem 'selleo-controller_tests'
endthen bundle
Notice For rspec-rails version 2.x put this config to spec/spec_helper.rb, for 3.x to: spec/rails_helper.rb
#...
require 'selleo_controller_tests'
#...
RSpec.configure do |config|
#...
config.include(Selleo::XhrPersistence)
config.with_options(type: :controller) do |config|
config.extend Selleo::ControllerMacros
end
#...Notice: All shared examples suppose, you have defined the call_request block, in which you call the action.
For instance:
describe '#show' do
let(:call_request) { get :show, id: 123 }
enddescribe '#create' do
it_behaves_like 'an action redirecting to', -> { specific_path }
enddescribe '#create' do
it_behaves_like 'an action redirecting to back'
enddescribe '#show' do
it_behaves_like 'an action rendering view'
endIt checks if the action rendered view which is specified in the describe block.
describe '#show' do
context 'inside a nested block' do
it_behaves_like 'an action rendering view'
end
enddescribe '#show' do
it_behaves_like 'an action rendering view' do
let(:view) { 'custom_view' }
end
endBy overriding the view variable, you can specify a view, which is rendered in the action.
These shared examples allows you to check, if the request has created a new object. It requires to set the 'attributes' let block, with new attributes for the object.
describe '#create' do
let(:attributes) { attributes_for(:person) }
let(:call_request) { post :create, person: attributes }
it_behaves_like 'an action creating object', [:name, :age]
endA class of an object is evaluated from the described class name, e.g.
if the described class is: UsersController, the created object has class User.
If the object's class is different, you can override it:
describe '#create' do
let(:attributes) { attributes_for(:person) }
let(:call_request) { post :create, person: attributes }
it_behaves_like 'an action creating object', [:name, :age] do
let(:model_class) { Admin }
end
endAlso, sometimes it's necessary to specify the new attributes:
describe '#create' do
let(:attributes) { attributes_for(:person) }
let(:call_request) { post :create, person: attributes }
it_behaves_like 'an action creating object', [:name, :age] do
let(:new_attributes) { {admin?: true} }
end
endWhen you find that the object should be created, but you don't want to specify the fields you can easily omit them:
describe '#create' do
let(:attributes) { attributes_for(:person) }
let(:call_request) { post :create, person: attributes }
it_behaves_like 'an action creating object'
endSometimes, when for example you have roles in your system, you can specify, that the action creating object should fail, not to create a new object. This usage will only check if the object has been not created. There is no need for passing fields of the object. You can specify it in this way:
describe '#create' do
let(:attributes) { attributes_for(:person) }
let(:call_request) { post :create, person: attributes }
it_behaves_like 'an action creating object', expect_failure: true
endThese shared examples allows you to check, if the request has updated an existing object. It requires to set the 'attributes' let block, with new attributes for the object. Also, it requires that the attributes will change, so the factory should use sequences for generating different values each time.
describe '#update' do
let!(:person) { create(:person) }
let(:attributes) { attributes_for(:person) }
let(:call_request) { patch :update, id: person.id, person: attributes }
it_behaves_like 'an action updating object', [:name, :age]
endA name of updated model is evaluated from the described class' name, e.g.
If the described class is: UsersController, the model's name is: 'user'.
If the object's class is different, you can override it:
describe '#update' do
let!(:person) { create(:person) }
let(:attributes) { attributes_for(:person) }
let(:call_request) { patch :update, id: person.id, person: attributes }
it_behaves_like 'an action updating object', [:name, :age] do
let(:model_name) { :admin }
end
endAlso, sometimes it's necessary to specify the new attributes:
describe '#update' do
let!(:person) { create(:person) }
let(:attributes) { attributes_for(:person) }
let(:call_request) { patch :update, id: person.id, person: attributes }
it_behaves_like 'an action updating object', [:name, :age] do
let(:new_attributes) { {admin: true} }
end
endSometimes, when for example you have roles in your system, you can specify, that the action updating object should fail - not to update the existing object. This usage will check if specified attributes have not changed after calling request. You can specify it in this way:
describe '#update' do
let!(:person) { create(:person) }
let(:attributes) { attributes_for(:person) }
let(:call_request) { patch :update, id: person.id, person: attributes }
it_behaves_like 'an action updating object', [:name, :age], expect_failure: true
endThese shared examples allows you to check, if the request has destroyed an existing object. It evaluates a name of object which should be destroyed from the described class name.
describe '#destroy' do
let!(:person) { create(:person) }
let(:call_request) { delete :destroy, id: person.id }
it_behaves_like 'an action destroying object'
endWhen you want to specify a name of object which should be destroyed you can do it in two ways. Firstly, by specifying the object:
describe '#destroy' do
let!(:person) { create(:person) }
let!(:admin) { create(:admin) }
let(:call_request) { delete :destroy, id: person.id }
it_behaves_like 'an action destroying object' do
let(:object) { admin }
end
endSecondly, by specifying name of the object which should be destroyed. This way, the shared example looks for an object, with given name:
describe '#destroy' do
let!(:person) { create(:person) }
let!(:admin) { create(:admin) }
let(:call_request) { delete :destroy, id: person.id }
it_behaves_like 'an action destroying object' do
let(:object) { :admin }
end
endWhen you find that the action should fail - should not destroy the object, you can do it in this way:
describe '#destroy' do
let!(:person) { create(:person) }
let(:call_request) { delete :destroy, id: person.id }
it_behaves_like 'an action destroying object', expect_failure: true
endThese shared examples specify if the object should be handled with a service object.
It checks if the service object has been initialized and saved in the proper way.
The service object must respond to the save method.
By default it expects the param for the initialize method of the service object is an object which has a class
named by the service object, e.g. a service with class PersonCreator expects an object of the Person class.
describe '#create' do
let(:attributes) { attributes_for(:person) }
let(:call_request) { post :create, person: attributes }
let!(:schedule) { create(:schedule) }
it_behaves_like 'action handled with service object', PersonCreator
endWhen you want to use custom params for the initialize method of the service object:
describe '#create' do
let(:attributes) { attributes_for(:person) }
let(:call_request) { post :create, person: attributes }
let!(:schedule) { create(:schedule) }
it_behaves_like 'action handled with service object', PersonCreator do
let(:parameters) { [kind_of(Admin), admin: true] }
end
end