Controller test with stub using Rails 4 and Rspec 3.4 - ruby-on-rails

In my controller I have the following method:
def set_provider
#provider = Provider.find_by(id: params[:id])
return #provider unless #provider.active
#provider = IntegrationProvider.new(#provider.slug).proxy_to.find_by(id: params[:id])
end
Which I am trying cover with the following test:
context 'active' do
before do
#provider = FactoryGirl.create(:provider, :active)
get :show, id: #provider.id
end
it '200' do
expect(response.status).to be(200)
end
it 'assigns #provider' do
expect(assigns(:provider)).to eq(#provider)
end
end
The IntegrationProvider class is basically a child model of the Provider model. Since these can be dynamic, and I am using FactoryGirl I am thinking a stub would work best here. In the code base the slugs correspond to folders so I do not want to test that other class in this controller class. I will save that for my lib class tests.
Question
How would you stub that IntegrationsProvider class so it returns the Provider model?

Something like this should work:
allow(IntegrationProvider).to receive_message_chain(:new, :proxy_to, :find_by).and_return(yourproviderobjecthere)

Related

Rspec: how to test Service Object method "call" which is called in Controller action create?

Can somebody help me with rspec testing method call in Service Object?
class UserEntitiesController < ApplicationController
def create
#result = UserEntities::Create.new(params).call
return render '/422.json.jbuilder', status: :unprocessable_entity unless #result
end
here is the service objects:
module UserEntities
class Create
attr_accessor :params
def initialize(params)
#params = params
end
def call
#user_entity = UserEntity.new(user_entity_params)
set_time
if #user_entity.save
#user_entity
else
error_result
end
end
private
def error_result
false
end
def user_entity_params
#params.require(:user_entity).permit(:information,
:destroy_option,
:reviews)
end
def set_time
if #params[:available_days].present?
#user_entity.termination = Time.now + #params[:available_days].days
end
end
end
end
I tried to find information how to do this, but there are not so many.
Also i read some
You can certainly write a unit test to test the Service Object standalone
In this case, create a file spec/services/user_entities/create_spec.rb
describe UserEntities::Create do
let(:params) { #values go here }
context ".call" do
it "create users" do
UserEntities::Create.new(params).call
# more test code
end
# more tests
end
end
Later in the controller tests, if you are planning to write such, you do not need to test UserEntities::Create instead you can just mock the service object to return the desired result
describe UserEntitiesController do
before do
# to mock service object in controller test
allow(UserEntities::Create).to receive(:new)
.and_return(double(:UserEntities, call: "Some Value"))
end
# controller tests go here
end
As a supplement to #bibin answer.
If you want to mock some instance's method renturn:
allow_any_instance_of(UserEntities::Create).to receive(:call).and_return("some value")
if you want to raise a eror:
allow_any_instance_of(UserEntities::Create).to receive(:call).and_raise("boom")

How to test that a class is called in a controller method with RSpec

I am testing my controller to ensure that a library class is called and that the functionality works as expected. NB: This might have been asked somewhere else but I need help with my specific problem. I would also love pointers on how best to test for this.
To better explain my problem I will provide context through code.
I have a class in my /Lib folder that does an emission of events(don't mind if you don't understand what that means). The class looks something like this:
class ChangeEmitter < Emitter
def initialize(user, role, ...)
#role = role
#user = user
...
end
def emit(type)
case type
when CREATE
payload = "some payload"
when UPDATE
payload = "some payload"
...
end
send_event(payload, current_user, ...)
end
end
Here is how I am using it in my controller:
class UsersController < ApplicationController
def create
#user = User.new(user_params[:user])
if #user.save
render :json => {:success => true, ...}
else
render :json => {:success => false, ...}
end
ChangeEmitter.new(#user, #user.role, ...).emit(ENUMS::CREATE)
end
end
Sorry if some code doesn't make sense, I am trying to explain the problem without exposing too much code.
Here is what I have tried for my tests:
describe UsersController do
before { set_up_authentication }
describe 'POST #create' do
it "calls the emitter" do
user_params = FactoryGirl.attributes_for(:user)
post :create, user: user_params
expect(response.status).to eq(200)
// Here is the test for the emitter
expect(ChangeEmitter).to receive(:new)
end
end
end
I expect the ChangeEmitter class to receive new since it is called immediately the create action is executed.
Instead, here is the error I get:
(ChangeEmitter (class)).new(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
What am I missing in the above code and why is the class not receiving new. Is there a better way to test the above functionality? Note that this is Rspec. Your help will be much appreciated. Thanks.
You need to put your expect(ChangeEmitter).to receive(:new) code above the post request. When you are expecting a class to receive a method your "expect" statement goes before the call to the controller. It is expecting something to happen in the future. So your test should look something like:
it "calls the emitter" do
user_params = FactoryGirl.attributes_for(:user)
expect(ChangeEmitter).to receive(:new)
post :create, user: user_params
expect(response.status).to eq(200)
end
EDIT
After noticing that you chain the "emit" action after your call to "new" I realized I needed to update my answer for your specific use case. You need to return an object (I usually return a spy or a double) that emit can be called on. For more information on the difference between spies and doubles check out:
https://www.ombulabs.com/blog/rspec/ruby/spy-vs-double-vs-instance-double.html
Basically a spy will accept any method called on it and return itself whereas with a double you have to specify what methods it can accept and what is returned. For your case I think a spy works.
So you want to do this like:
it "calls the emitter" do
user_params = FactoryGirl.attributes_for(:user)
emitter = spy(ChangeEmitter)
expect(ChangeEmitter).to receive(:new).and_return(emitter)
post :create, user: user_params
expect(response.status).to eq(200)
end

Dynamic rendering with Rails 5 and Rspec

I am trying to test that my controller renders the correct status codes via request specs. This application uses a bit of meta-programming with dynamic class names to render views. How can I stub the render call below to return the correct status code desired for my spec?
Rspec Spec Snippet
context 'renders 200' do
let(:provider_slug) { create(:provider, :active).slug }
let(:template) { "providers/v1/#{provider_slug}/new" }
let(:layout) { "providers/v1/#{provider_slug}" }
let(:provider_double) do
instance_double(
ProviderRouter,
valid?: true,
form_model: ProviderFormModel
)
end
before do
allow(ProviderRouter).
to receive(:new).with(version: 'V1', provider_slug: provider_slug).
and_return(provider_double)
allow(described_class).to receive(:render_new_form).and_return(true)
get route
end
it 'true' do
expect(response.status).to be(200)
end
end
Controller Snippet
class V1::ProvidersController < ApplicationViewController
before_action :init_provider, :init_form_types, :validate_provider
def new
#provider_form_model = provider_router.form_model.new
render_new_form
end
private
attr_reader :provider_slug, :provider_path, :provider_router, :provider_model
def render_new_form
render template: "providers/v1/#{provider_slug}/new", layout: "providers/v1/#{provider_slug}"
end
Updated for Answer Below
context 'renders 200' do
let(:provider_slug) { create(:provider, :active).slug }
let(:provider_double) do
instance_double(
ProviderRouter,
valid?: true,
form_model: ProviderFormModel
)
end
before do
allow(ProviderRouter).
to receive(:new).with(version: 'V1', provider_slug: provider_slug).
and_return(provider_double)
allow(controller).to receive(:provider_slug).and_return(provider_slug)
allow(controller).to receive(:render).and_call_original
allow(controller).to receive(:render).
with(template: "providers/v1/#{provider_slug}/new", layout: "providers/v1/#{provider_slug}") do
controller.render plain: '200 [OK]'
end
get "/v1/providers/#{provider_slug}"
end
it 'true' do
expect(response.status).to be(200)
end
end
The formal answer would be that you shouldn't stub it as you would be stubbing behaviour of the object under test.
You should rather provide a provider_slug to be used for the test.
Technically, it would be possible to do this:
allow(controller) # controller is the instance of the ProvidersController used under the hood
.to_receive(:render_new_form)
.and_return("some bogus value")
But this would lead to rails trying to render the default template as no rendering has happened yet. It would thus be helpful to actually call the render method which can be achived by:
# we call the render method in our stub and thus have to be able to call the original
allow(controller)
.to receive(:render)
.and_call_original
allow(controller)
.to_receive(:render) # not render_new_form
.with(template: anything, layout: anything) do
controller.render plain: '200 [OK]'
end

Testing letsrate controller. RSpec

I want to test letsrate generated controller.
But I don;t know how do this, because I can not understand how it works.
rater_controller.rb
class RaterController < ApplicationController
def create
if user_signed_in?
obj = params[:klass].classify.constantize.find(params[:id])
obj.rate params[:score].to_i, current_user, params[:dimension]
render :json => true
else
render :json => false
end
end
end
UPDATE
Letsrate is a gem for rails
rater_controller_spec.rb
require 'rails_helper'
describe RaterController do
describe 'POST create' do
let(:valid_attributes)do {
klass: 'Hotel',
dimension: 'rating',
score: '5'
}
end
it 'user signed in' do
user = create(:user)
hotel = create(:hotel)
post :create, { rate: valid_attributes, rater_id: user.id, rateble_id: hotel.id }
sign_in user
end
end
end
The source code you posted makes it pretty obvious how it works. You need to call the create action in RaterController with these params: klass, id, score, dimension. Let's say the klass param is "Restaurant", which is also the name of an ActiveRecord model class. The controller will query the database for a restaurant with the specified ID. Then it will call the rate method on that object with the specified parameters, which presumably inserts a row into the database representing the user's new rating. To test it, you could simply call the controller action and then check to make sure the row got added to the database.

How to call the create action from the controller in RSpec

I have a controller create action that creates a new blog post, and runs an additional method if the post saves successfully.
I have a separate factory girl file with the params for the post I want to make. FactoryGirl.create calls the ruby create method, not the create action in my controller.
How can I call the create action from the controller in my RSpec? And how would I send it the params in my factory girl factories.rb file?
posts_controller.rb
def create
#post = Post.new(params[:post])
if #post.save
#post.my_special_method
redirect_to root_path
else
redirect_to new_path
end
end
spec/requests/post_pages_spec.rb
it "should successfully run my special method" do
#post = FactoryGirl.create(:post)
#post.user.different_models.count.should == 1
end
post.rb
def my_special_method
user = self.user
special_post = Post.where("group_id IN (?) AND user_id IN (?)", 1, user.id)
if special_post.count == 10
DifferentModel.create(user_id: user.id, foo_id: foobar.id)
end
end
end
Request specs are integration tests, using something like Capybara to visit pages as a user might and perform actions. You wouldn't test a create action from a request spec at all. You'd visit the new item path, fill in the form, hit the Submit button, and then confirm that an object was created. Take a look at the Railscast on request specs for a great example.
If you want to test the create action, use a controller spec. Incorporating FactoryGirl, that would look like this:
it "creates a post" do
post_attributes = FactoryGirl.attributes_for(:post)
post :create, post: post_attributes
response.should redirect_to(root_path)
Post.last.some_attribute.should == post_attributes[:some_attribute]
# more lines like above, or just remove `:id` from
# `Post.last.attributes` and compare the hashes.
end
it "displays new on create failure" do
post :create, post: { some_attribute: "some value that doesn't save" }
response.should redirect_to(new_post_path)
flash[:error].should include("some error message")
end
These are the only tests you really need related to creation. In your specific example, I'd add a third test (again, controller test) to ensure that the appropriate DifferentModel record is created.

Resources