How can I determine the subject of an rspec controller test? - ruby-on-rails

So I don't have a great reason for needing to know this other than curiosity - the BEST reason - but I'm not sure what's going on here.
Background:
I'm working through the RSpec book and updating the examples.
On Chapter 24 - Rails Controllers there's a test for a messages controller.
## spec/controllers/messages_controller_spec.rb ##
require 'spec_helper'
describe MessagesController do
describe "POST create" do
let(:message) { mock_model(Message).as_null_object }
before do
Message.stub(:new).and_return(message)
end
# Then a bunch of Tests...
context "when the message fails to save" do
before do
message.stub(:save).and_return(false)
post :create
end
it "assigns #message" do
assigns[:message].should eq(message)
end
it "renders the new template" do
response.should render_template("new")
end
end
end
end
This goes along with the messages controller:
## app/controllers/messages_controller.rb ##
class MessagesController < ApplicationController
def create
#message = Message.new(params[:message])
if #message.save
flash[:notice] = "The message was saved successfully"
redirect_to action: "index"
else
render "new"
end
end
end
When I run the tests:
The test passes with response.
it "renders the new template" do
response.should render_template("new")
end
The test also passes with subject.
it "renders the new template" do
subject.should render_template("new")
end
The test Also passes with page
it "renders the new template" do
page.should render_template("new")
end
The test ALSO passes with NOTHING
it "renders the new template" do
should render_template("new")
end
In case it helps anyone make heads or tails of this, the config/routes.rb just has resources :messages
Why do all those tests pass? What am I actually testing? Are 'page', 'subject', and ' ' just synonyms for response?
Does it matter as long as my tests pass?

By default, the subject would be referencing the class, which is the MessagesController.
Not defining a subject in the last test example, will implicitly set the subject to be MessagesController.

From a binding.pry, it appears that subject is an instance of the controller class:
[2] pry(#<RSpec::ExampleGroups::MyController::DescribeString::ContextString>)> subject.is_a? Class
=> false
[3] pry(#<RSpec::ExampleGroups::MyController::DescribeString::ContextString>)> subject.is_a? Users::SessionsController
=> true

Related

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

Rails Controller testing

I am doing the thoughtbot intro to testing program. Im not sure how to test for what they want.
Below is my test.
require "rails_helper"
describe PeopleController do
describe "#create" do
context "when person is valid" do
it "redirects to #show" do
post :create, FactoryGirl.build_stubbed(:person)
expect(response).to redirect_to(show_people_path)
end
end
context "when person is invalid" do
it "redirects to #new" do
pending "create this test"
end
end
end
end
I am of course using factory girl. I have tried several methods. I really don't know hoe to test this controller.
Any insights would be great.
I would create an 'invalid' person using the FactoryGirl, and send it as a parameter to the post :create.
To create an invalid person record, why don't you use nested factories in FactoryGirl? Depending on the validation in your model, you can simply do something like:
spec/factories/person.rb
FactoryGirl.define do
factory :person do
...
factory :invalid_person do
...
email nil
...
end
end
end
in your test
context "when person is invalid" do
it "redirects to #new" do
post :create, FactoryGirl.build_stubbed(:invalid_person)
expect(response).to redirect_to action: :new
end
end

rspec controller test expecting route

I am trying to test that on the creation of a post, the user is redirected to deployments path. I have added in
config.include Rails.application.routes.url_helpers
to extend rails routes to rspec. But my test still fails with the following error
1) Failure/Error: expect(response).to redirect_to(path)
Expected response to be a redirect to <http://test.host/deployments/new> but was a redirect to <http://test.host/deployments/new.1473>.
Expected "http://test.host/deployments/new" to be === "http://test.host/deployments/new.1473".
# -e:1:in `<main>'
Here is the test:
describe "post create" do
before do
allow(model).to receive(:new).and_return(instance)
end
context "where all is not well" do
before do
allow(instance).to receive(:save).and_return(false)
post :create, params_new_instance
end
sets_flash(:error)
it "should render the new form" do
expect(response).to render_template("projects/new")
end
end
context "where all is well" do
before do
allow(instance).to receive(:save).and_return(true)
post :create, params_new_instance
end
sets_flash(:notice)
it "redirects to new_deployments_path" do
expect(controller.controller_path).to eq(new_deployment_path)
end
end
end
project controller
def create
#project=Project.new(params_project)
if #project.save
record_saved
return redirect_to(new_deployment_path(#project))
else
check_for_errors
return render('/projects/new')
end
end
why is this failing? am i approaching this in the right way?
thanks in advance
Are you sure that new_deployments_path instead of new_deployment_path
This is my first suggestion. Them can be more but not enough information: routes.rb and deployments controller for example

How to create a route for testing purposes?

I'm writing tests with rspec for my application controller in my rails app (written in Rails 4) and I'm running into a problem where it doesn't recognize the route for the HTTP request I'm sending. I know there's a way to do this using MyApp::Application.routes but I'm not able to get it working.
#application_controller_spec.rb
require 'spec_helper'
class TestController < ApplicationController
def index; end
end
describe TestController do
before(:each) do
#first_user = FactoryGirl.create(:user)
# this is to ensure that all before_filters are run
controller.stub(:first_time_user)
controller.stub(:current_user)
end
describe 'first_time_user' do
before(:each) do
controller.unstub(:first_time_user)
end
context 'is in db' do
before(:each) do
#user = FactoryGirl.create(:user)
controller.stub(:current_user).and_return(#user)
end
it 'should not redirect' do
get :index
response.should_not be_redirect
end
end
context 'is not in db' do
context 'session[:cas_user] does not exist' do
it 'should return nil' do
get :index
expect(assigns(:current_user)).to eq(nil)
end
end
it "should redirect_to new_user_path" do
controller.stub(:current_user, redirect: true).and_return(nil)
get :index
response.should be_redirect
end
end
end
The error I'm getting right now is
No route matches {:action=>"index", :controller=>"test"}
I would add the test#index route to config/routes.rb, but it doesn't recognize the Test Controller, so I want to do something like
MyApp::Application.routes.append do
controller :test do
get 'test/index' => :index
end
end
but I'm not sure where to add this or if this even works in rspec. Any help would be great!
If you are trying to test your ApplicationController, see this RSpec documentation about it. You will need to define methods like index inside the test, but it works well.

How to DRY up RSpec tests shared by different actions in same controller

I have the following tests that I want tested from various actions in the same controller. How can I DRY this up? In the comments below you'll see that the test should call a different method and action depending on which action I'm testing.
shared_examples_for "preparing for edit partial" do
it "creates a new staff vacation" do
StaffVacation.should_receive(:new)
get :new
end
it "assigns #first_day_of_week" do
get :new
assigns(:first_day_of_week).should == 1
end
end
describe "GET new" do
# i want to use 'it_behaves_like "preparing for edit partial"'
# and it should use 'get :new'
end
describe "GET edit" do
# i want to use 'it_behaves_like "preparing for edit partial"'
# but it should use 'get :edit' instead
end
describe "POST create" do
# on unsuccessful save, i want to use 'it_behaves_like "preparing for edit partial"'
# but it should use 'post :create' instead
end
You could do something like this:
shared_examples_for "preparing for edit partial" do
let(:action){ get :new }
it "creates a new staff vacation" do
StaffVacation.should_receive(:new)
action
end
it "assigns #first_day_of_week" do
action
assigns(:first_day_of_week).should == 1
end
end
context 'GET new' do
it_should_behave_like 'preparing for edit partial' do
let(:action){ get :new }
end
end
context 'GET edit' do
it_should_behave_like 'preparing for edit partial' do
let(:action){ get :edit }
end
end
context 'POST create' do
it_should_behave_like 'preparing for edit partial' do
let(:action){ post :create }
end
end
Or, you could use some kind of loop for the examples:
['get :new', 'get :edit', 'post :create'].each do |action|
context action do
it "creates a new staff vacation" do
StaffVacation.should_receive(:new)
eval(action)
end
it "assigns #first_day_of_week" do
eval(action)
assigns(:first_day_of_week).should == 1
end
end
end
One option might be to provide a module mix-in with a method that has your spec inside it.
include Auth # This is your module with your generalized spec inside a method
it "redirects without authentication" do
unauthorized_redirect("get", "new")
end
Then, in our method, we could do a loop through different types of authorization:
module Auth
def unauthorized_redirect(request, action)
[nil, :viewer, :editor].each do |a|
with_user(a) do
eval "#{request} :#{action}"
response.should redirect_to login_path
# whatever other expectations
end
end
end
end

Resources