How do I change the default controller in Rspec? - ruby-on-rails

I need authentication to pass the test successfully.
describe PostsController do
let(:user) { create(:user) }
describe 'POST #create' do
let(:create_post) { post :create, params: { post: attributes_for(:post) } }
before do
post sessions_path, params: { login: user.email, password: user.password }
end
context 'with valid attributes' do
it 'return OK' do
create_post
expect(response).to have_http_status(200)
end
end
end
post session_path - this does not work and generates an error:
No route matches {:action=>"/sessions", :controller=>"posts", :login=>"jack#example.com", :password=>"qwerty"}
How do I change default PostsController to SessionsController in the before block?

In controller specs you talk only to controller under test. If you need other controllers to create a few prerequisite objects first - create them directly in the db. If you need authenticated user - set session[:user_id] directly (or however it is your authentication works). See this answer, for example.
What you attempted to do is called a "feature spec" (or "integration spec"). It's a different type of spec and you should write some of those as well.

Related

Error occuring in testing my controller with RSPEC using shoulda matchers especially create i can't able to test save functionality

I am testing my controller with RSPEC using shoulda matchers while i came across the create method in my controller i cant test the save function if i try to do that i go the error
Expected response to be a <3XX: redirect>, but was a <200: OK>
i have attached my controller part and testing and route
In testing
RSpec.describe "routes for home", type: :routing do
describe 'post #create' do
before do
post :create , params: params
end
context 'when the params are correct' do
let(:params) { { restaurant: { restaurantname: "Buhari" ,location_id: 1} } }
it 'is expected save successfully and redirect_to gridpage' do
expect(assigns[:restaurant].save).to redirect_to(gridurl_path)
end
end
end
end
In controller
def create
# render plain: params
#restaurant=Restaurant.new(restaurant_params)
if #restaurant.save
redirect_to gridurl_path
else
render 'index'
end
end
In routes
post "/home/create", to: "home#create", as: :createurl
get '/home/grid', to: 'home#grid',as: :gridurl
Thank you in advance
First I suggest you read https://relishapp.com/rspec/rspec-rails/docs/controller-specs and also the other docs. They will give you a good starting point on how to test stuff with rspec.
When you look at a controller action, you are not interested on who's doing what (i.e assigns[:restaurant]) - you want to see if a redirect happens, if something is saved in the DB, etc. Think of it from the perspective of a user calling that endpoint. Does the user know all of the internals?
Here is how it should look like:
describe "routes for home", type: :controller do
describe 'post #create' do
context 'when the params are correct' do
let(:params) { { restaurant: { restaurantname: "Buhari" ,location_id: 1} } }
it 'is expected save successfully and redirect_to gridpage' do
post :create, params: params
expect(response).to redirect_to('/home/grid')
end
end
end
end

How to access instance variables to test `receive` in a spec?

I have the following spec fragment:
it 'should create company and user' do
company_iv = assigns(:company)
user_iv = assigns(:user)
expect(subject).to receive(:create_timeline_event).with(company_iv, user_iv)
expect { post :create, params }.to change { User.count }.by(1).and change { Company.count }.by(1)
and traditionally use the receive syntax to test calling a method. I normally call it before the call to post in the above fragment. How would I access the instance variable of the user and the company for this spec?
Looks like you're trying to jam a few different tests into a single it statement. Here's how I would approach this:
it 'creates company and user' do
expect { post :create, params }
.to change { User.count }.by(1)
.and change { Company.count }.by(1)
end
it 'assigns instance variables' do
post :create, params
expect(assigns(:company)).to eq(Company.last)
expect(assigns(:user)).to eq(User.last)
end
it 'calls create_timeline_event with newly created company and user' do
allow(some_object).to receive(:create_timeline_event)
post :create, params
expect(some_object)
.to have_received(:create_timeline_event)
.with(Company.last, User.last)
end
Note that these tests are going to be slow because they hit the database. Another approach to this is to use mocks. That would look something like this:
let(:params) { ... }
let(:company) { instance_double(Company) }
let(:user) { instance_double(User) }
before do
allow(Company).to receive(:create).and_return(company)
allow(User).to receive(:create).and_return(user)
allow(some_object).to receive(:create_timeline_event)
post :create, params
end
it 'creates company and user' do
expect(Company).to have_received(:create).with(company_params)
expect(User).to have_received(:create).with(user_params)
end
it 'assigns instance variables' do
expect(assigns(:company)).to eq(company)
expect(assigns(:user)).to eq(user)
end
it 'calls create_timeline_event with newly created company and user' do
expect(some_object)
.to have_received(:create_timeline_event)
.with(company, user)
end
These tests do not hit the database at all, meaning that they'll execute much faster.

How to test activeadmin AuthorizationAdapter?

I have a custom AutorizationAdapter that I would like to test using RSpec:
class AdminAuthorization < ActiveAdmin::AuthorizationAdapter
def authorized?(_action, _subject = nil)
user.admin?
end
end
Initially I used a custom method but since I'm using Devise, using a custom AuthorizationAdapter seemed to be the way to go.
How would you go about testing it ? I tought one way to test it is to create a request spec for one of the controller and test for status code & redirection, something like that:
require 'rails_helper'
RSpec.describe 'AdminUsers', type: :request do
describe 'GET /admin_users' do
context 'admin' do
let(:admin_user) { create(:admin_user) }
before { sign_in super_user }
get admin_users_path
expect(response).to have_http_status(200)
end
context 'non admin' do
let(:user) { create(:user) }
before { sign_in user }
it 'redirects to the login page' do
get admin_users_path
expect(response).to have_http_status(302)
expect(response).to redirected_to '/admin/login'
end
end
context 'non logged in user' do
it 'redirects to the login page' do
get admin_users_path
expect(response).to have_http_status(302)
expect(response).to redirected_to '/admin/login'
end
end
end
end
I'm not sure this is the way to go.
These look reasonable to me. You can also look at the unit and feature specs that are in the ActiveAdmin test suite. However, AuthorizationAdapter itself is a PORO so you should be able to unit test in isolation: in the example given above that would be a fairly trivial test.

Testing with Rspec3 controllers with belongs_to association using instance_double

I'm new in testing and learning Rspec, and I can't git it working.
(I have read the book Effective testing with Rspec3, and many tutorials ...also pluralsight.com)
The situation is very simple. In a Companies controller I want to test de Create method, the company model belongs_to user, and is this the code:
I think the problem is when execute
in test: expect(Company).to receive(:new).with(company_params)
or in controller: #company.user=helpers.user
Controller:
class CompaniesController < SessionsController
def create
#company=Company.new(company_params)
#company.user=helpers.user
if #company.save()
redirect_to companies_path
else
render :edit
end
end
and Rspec:
RSpec.describe CompaniesController, type: :controller do
let(:user) { instance_double(User) }
before do
allow_any_instance_of(ApplicationHelper).to receive(:user){user}
allow(controller).to receive(:authorize){true}
end
describe 'Authenticated user with companies' do
let(:company_params) { {company:{name:"Albert",domain:"www.albert.com"}} }
let(:company) { instance_double(Company) }
before do
allow(Company).to receive(:new){company}
end
describe 'POST #create' do
context "with valid data" do
before { allow(company).to receive(:save){true} }
it "redirects to companies_path" do
expect(Company).to receive(:new).with(company_params)
expect(company).to receive(:user=).with(user)
post :create, params:{company: company_params}
expect(response).to redirect_to(companies_path)
end
My intention is very simple: Use instance_double to mock (or stub) #company, and Company.new, using instance double...to test the create action, and simulate the "save()" returning true...etc
I do not know if I explain myself very well, but given the create action of controlloer , how to test using mocks ans stubs, instance_double?
Thanks
First of all let me explain what we need to test here
def create
#company=Company.new(company_params)
#company.user=helpers.user
if #company.save()
redirect_to companies_path
else
render :edit
end
end
We are testing create action of a controller. First let us see what this action does? It's just takes comapany_params as input and create a company record in database.
Testing also goes like the same, we need to just pass the input that action required, and need to check whether it's creating record in database or not.
RSpec.describe CompaniesController, type: :controller do
let(:user) { instance_double(User) }
before do
# all your authentication stubing goes here
allow_any_instance_of(ApplicationHelper).to receive(:user){user}
allow(controller).to receive(:authorize){true}
end
describe 'POST#create' do
context 'with valid attributes' do
before do
post :create, { company:{ name:"Albert", domain:"www.albert.com"} }
end
it 'responds with success' do
expect(response.status).to eq(302)
end
it 'creates company' do
company = Company.find_by(name: "Albert")
expect(assigns(:company)).to eq(company)
expect(response).to redirect_to(companies_path())
end
end
context 'with invalid attributes' do
before do
post :create, { company:{ name:"", domain:""} }
end
it 'renders new template' do
expect(response).to render_template(:edit)
end
end
end
end
No need to sub anything here. As per my knowledge, Only when we use any lib classes / background jobs / third party libraries code inside action then we need to stub those code. Because for all those, we will write specs separately. So no need to test again here that's why we'll do stubing.
Thanks to Narsimha Reddy, I have better ideas about how to test.
Eventhough, if I want to stub
#company=Company.new(company_params)
#company.user=helpers.user
if #company.save()
For testing only de create's response , the solution was in a good use of parameters, and allowing allow(company).to receive(:user=) for the belongs_to association
let(:company_params) {{company:{name:"Albert",domain:"www.albert.com"}}}
let(:ac_company_params) {ActionController::Parameters.new(company_params).require(:company).permit!}
let(:company) { instance_double(Company) }
before do
allow(Company).to receive(:new){company}
allow(company).to receive(:user=)
allow(company).to receive(:save){true}
end
it "redirects to companies_path" do
expect(Company).to receive(:new).with(ac_company_params)
expect(company).to receive(:user=).with(user)
post :create, params: company_params
expect(response).to redirect_to(companies_path)
end

How to mock a service object? should I mock it?

I have a service object called ResetPassword that handles all the logic for the ResetPassword Controller create action. I also have already tested the service object. Should I mock the service object? I figure I should since it's ready tested and it would cut down on running specs. My test code so far for the controller is below. Not sure if it should be written this way.
require 'spec_helper'
describe ResetPasswordController do
describe "POST create" do
context "when email matches a user" do
let(:user) { Fabricate(:user) }
it "calls password_reset on PasswordReset" do
ResetPassword.stub(:reset_password)
ResetPassword.any_instance.should_receive(:reset_password)
post :create, email: user.email
end
it "redirects to root path" do
post :create, email: user.email
expect(response).to redirect_to root_path
end
end
context "when email doesn't match a user" do
it "redirects to new"
it "displays a flash error"
end
end
end
I think you should mock the service in your controller, but mock it by injecting a mock instead of stubbing on the class or any_instance
Your controller could look like this
class ResetPasswordController < ApplicationController
def create
reset_password_service.reset_password(params[:email])
end
def reset_password_service
#reset_password_service ||= ResetPassword.new
end
def reset_password_service=(val)
#reset_password_service = val
end
end
Then in your spec you can
before { controller.reset_password_service = password_service }
let(:password_service) { double("PasswordService", reset_password: nil) }
it "does something good" do
post :create, email: "foo"
expect(password_service).to have_received(:reset_password).with("foo")
end
Or even better, use an instance_double instead. That will also check that the stubbed methods actually exists on the stubbed class. This is available from RSpec 3.0.0.beta*
let(:password_service) { instance_double(PasswordService, reset_password: nil) }
you can use mockito to mock your service and imockit multiple services with mockito.

Resources