Ruby On Rails Function Testing :index with a polymorphic model - ruby-on-rails

I have a polymorphic model called Address, I am trying to currently write some basic function tests for this model and controller. For the controller I am at a loss on how to go about this. For example I have another model called Patient, each Patient will have an address, so i have started writing the following function test, but i have no idea how to use "get" with a nested polymorphic resource. Now I was able to find some polymorphic test information on Fixtures here: http://api.rubyonrails.org/classes/Fixtures.html
but this will not help me test against the index. Any help is much appreciated im at a total and complete loss here.
FILE: test/functional/addresses_controller_test.rb
require 'test_helper'
class AddressesControllerTest < ActionController::TestCase
setup do
#address = addresses(:of_patient)
#patient = patients(:one)
activate_authlogic
end
test "patient addresses index without user" do
get :index <<<<<<<<<<<< what goes here????
assert_redirected_to :login
end
end

Assuming your controller is setup the way I think it might be:
def index
if #current_user
#addresses = #current_user.addresses.all
else
redirect_to login_path
end
end
Then the test will probably look like this:
test "patient addresses index without user" do
get :index, :patient_id => #patient.id
assert_redirected_to :login
end
test "patient addresses with user" do
#current_user = #patient
get :index, :patient_id => #patient.id
assert_response :success
end
The thing to keep in mind is that the index method needs the patient_id to process.

Related

How to write Rspec for basic Show, Create, Destroy, Edit methods in rails (with or without FactoryGirl)

I have a basic user_controller.rb file like this:
class UserController < ApplicationController
def new
#user = User.new
end
def index
#user = User.all
end
def show
#user = User.find(params[:id])
end
def create
#user = User.new(user_params)
if #user.save
redirect_to #user
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(user_params)
redirect_to #user
else
render 'edit'
end
end
def destroy
#user = User.find(params[:id])
#user.destroy
redirect_to action: 'index'
end
private
def user_params
params.require(:user).permit(:name, :key, :desc)
end
end
This is my (model) user.rb file:
class User < ApplicationRecord
validates :name, presence: true
validates :key, uniqueness: true, presence: true
validates :desc, presence: true
end
And created a factories.rb file (in the specs folder):
FactoryGirl.define do
factory :user do
name "TestUser"
key "TKey"
desc "TestDescription"
end
end
I tried several ways to make the specs work but I can't because of the confusing syntax.
The only one which worked was (for the 'C' in the CRUD operations, the below file is user_controller_specs.rb):
require 'rails_helper'
require 'factory_girl_rails'
RSpec.describe UserController, :type => :controller do
let(:temp) { FactoryGirl.build(:user) }
describe "POST create" do
it "should redirect back to the index page" do
post :create, :user => { :user => temp }
expect(get: user_url(subdomain: nil)).to route_to(controller: "user", action: "index")
end
end
end
I skimmed through several tutorials to find what should be the correct approach for CRUD operations but didn't got any simple to understand specs. I am trying to write these in the specs/controllers folder but these are giving errors. What should be the correct syntax to write the specs?
PS: I'am new to Ruby/Rails and trying to write test cases with Rspec and FactoryGirl. Any help is appreciated.
Edit:
Maybe I framed the question wrongly... I'm more interested in the syntax part. If I get to know an example how to write one, I'll be able to write others by changing some tiny bits of logic here and there.... Let's say I have a basic test case just to see whether updating a user details is not returning an error because of validations, how should I write it with (or without) Factory Girl gem?
It's a pretty broad question, but in any kind of test, you want test whatever use cases you have available to you. Example--are there different paths users might follow from hitting a specific controller action.
So you want your test to cover the basics. When you hit the create action, is a user actually created? If the relevant params are missing, is an error thrown? Use cases will drive your expectations.
With rspec controllers specifically, you'll use the appropriate verb and the name of the action, and pass it whatever parameters are necessary.
post :create, :user => { :user => temp }
That basically says, "do a post request to my create an action and pass it the parameters inside these curly braces."
After running that rspec gives you access to the response. You can always log the response after a controller request to help you debug the situation: p response.
You'll follow up each type of request with an expectation. The expectation should answer the question: "What did I expect hitting this action to do?" If you were, for instance, hitting the user update action and passed a param to change the user's age to 21, your expectation might be something like:
expect(user.age).to eq(21)
A great resource is the rspec documentation on relish. https://relishapp.com/rspec
"How to" do a broad general thing is a tough question to answer like this. My advice would be to try to actually test one, log the failure case, and post those logs in a new question and people on SO can help you work through testing a particular action you're struggling with.

Testing POST #create in RSpec with nested attributes - can't get params hash correct

Have been spending some months trying to grok RSpec/TDD. Running into some challenges testing a controller with a nested attribute. I'm a bit fuzzy about how to properly set up and pass the parameters to the controller. I can build the controller so that it actually works as expected in the browser - I just can't seem to reverse engineer a test to confirm that.
Would appreciate any recommendations to a) fix the test below, and b) advise any better ways to do it (e.g. with mocks, stubs, etc).
Here's the basic model structure:
class School < ActiveRecord::Base
has_many :scholarships
end
class Scholarship < ActiveRecord::Base
belongs_to :school
end
I've configured the routes.rb as you'd expect, with:
resources :schools do
resources :scholarships, only: [:new, :create, :destroy]
end
In the controller, #new and #create are pretty standard for a Rails app:
class ScholarshipsController < ApplicationController
def new
#school = School.find(params[:school_id])
#scholarship = #school.scholarships.build
end
def create
#school = School.find(params[:school_id])
#scholarship = #school.scholarships.create(scholarship_params)
if #scholarship.save
flash[:success] = 'Scholarship created!'
redirect_to #school
else
render 'new'
end
end
private
def scholarship_params
params.require(:scholarship).permit(:name, ** Lots of params omitted for brevity **,
:notes, school: [:id])
end
end
The spec is where I can't seem to figure things out. For spec/controllers/scholarships_controller_spec.rb:
require 'rails_helper'
describe ScholarshipsController, type: :controller do
describe 'POST #create' do
context 'with valid attributes' do
before :each do
#school = create(:school)
#scholarship = #school.scholarships.create(FactoryGirl.build(:scholarship).attributes)
end
it 'receives :save' do
post :create, { scholarship: #scholarship.attributes, school: #school.id }
expect(#scholarship).to receive(:save)
end
end
end
end
When I run that test, I get the following error:
Failures:
1) ScholarshipsController POST #create with valid attributes receives :save
Failure/Error: post :create, scholarship: { attributes: #scholarship.attributes, school: #school.id } #school_id: #school.id, scholarship: #scholarship
ActionController::UrlGenerationError:
No route matches {:action=>"create", :controller=>"scholarships",
:scholarship=>{"id"=>"1", "name"=>"Dynamic Metrics Director Scholarship", *** Lots of parameters omitted for brevity ***
, "school_id"=>"2"}, :school=>"2"}
The parameters look correct to me. there's a set of attributes for the scholarship, and for the school. But the routing isn't working. I've tried a dozen different ways to try and get this to work. Heartened that I'm apparently passing a (more or less correct) parameters hash, but can't figure out quite where I'm going wrong.
****** EDIT ******
Updated in response to an answer posted below.
Changed the syntax of the spec as suggested by Srdjan:
it 'receives :save' do
post :create, "schools/#{#school.id}/scholarships", { scholarship: #scholarship.attributes, school_id: #school.id }
expect(#scholarship).to receive(:save)
end
This changes the error message. I assume that indicates that the parameters are being passed correctly, since it's no longer throwing an error related to routes/params..? Error message is:
1) ScholarshipsController POST #create with valid attributes receives :save
Failure/Error: expect(#scholarship).to receive(:save)
(#<Scholarship:0x007fe293b02598>).save(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
Just for good measure, here are the relevant routes, which I hadn't posted previously:
school_scholarships POST /schools/:school_id/scholarships(.:format) scholarships#create
new_school_scholarship GET /schools/:school_id/scholarships/new(.:format) scholarships#new
school_scholarship DELETE /schools/:school_id/scholarships/:id(.:format) scholarships#destroy
In your test, you're POST-ing to the wrong route. As setup in routes.rb, scholarship resources do not exist out of the context of a school resource.
In order to fix this, you have to answer a question: "Does it make sense for a user to access a scholarship record without having to specify a school?"
If the answer is yes, you can either copy the scholarships route and paste them outside of the schools resource block. This way, you can have access to scholarships without having to specify a school, but also with specifying a school.
If the answer to the question is no, then you need to fix your test as such:
it 'receives :save' do
post :create, "schools/#{#school.id}/scholarhips", { scholarship: #scholarship.attributes, school_id: #school.id }
expect(#scholarship).to receive(:save)
end

what exactly is this rspec stub :where and what does it do?

I am looking through some tests written by other people and i keep seeing ':where' in their tests. I guess its a stub, but just finding my feet with testing and want to know is it any different from a normal stubs, what does the naming imply?
describe "delete destroy" do
context "is not allowed by user" do
before do
allow(model).to receive(:where).and_return(no_instances)
allow(no_instances).to receive(:first).and_return(no_instances)
end
context "where the record is destroyed" do
before do
allow(instance).to receive(:destroy).and_return(true)
delete :destroy, params_id
end
sets_flash(:notice)
redirects_to('/airbrake_accounts')
end
context "where the record is not destroyed" do
before do
allow(instance).to receive(:destroy).and_return(false)
delete :destroy, params_id
end
sets_flash(:error)
redirects_to('/airbrake_accounts')
end
end
context "where the record is not found" do
before do
allow(model).to receive(:where).and_return(no_instances)
delete :destroy, params_id
end
sets_flash(:error)
redirects_to('/airbrake_accounts')
end
end
I can see what is going on here (I think), things like ':new' are controller actions right?
describe "photo 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("entries/new")
end
end
context "where all is well" do
before do
allow(instance).to receive(:save).and_return(true)
post :create, params_new_photo
end
sets_flash(:notice)
redirects_to ('/photos')
end
end
They are class or instance methods on the model. Lets just say that the model variable in your example is set to the Dog model and this is testing the DogController.
# model
class Dog
def where(params)
do_stuff
end
end
# controller
class DogController > ApplicationController
def destroy
#dogs = Dog.where(id: 1)
redirect :new
end
end
Now I want to test whats going on in my controller, but I don't want to test anything that my model actually does. I'm isolating a unit of my code to be tested. This is different than testing how it all works integrated together (google unit testing or integration testing).
In order to test just whats going on in my controller I stub the methods happening on my model to keep things isolated and clean. The way I do this is by stubbing it out in my controller spec. So In my DogControllerSpec I do:
before do
allow(Dog).to receive(:where).and_return([])
end
So I'm saying allow my dog class to receive the 'where' method call, but don't execute its logic, and instead return me an empty array.
Then I can setup up the code I'm actually testing which is that my destroy method renders new when called.
it "should render the new form" do
expect(response).to render_template("dogs/new")
end
The key here is really that you are attempting to decouple your tests, so that when you unit test your DogController#destroy you are not testing your Dog.where method. The reasoning being that if you change code in your Dog model it should not break specs in your DogControllerSpec.
They are using symbol #to_proc, I believe, to 'stub' calling the method #where on model. So that means the line:
allow(model).to receive(:where).and_return(no_instances)
is essentially
model.where #=> [].
:new is a controller action, but the way this code is using it is more like SomeClass.new, i.e. the method to create an instance of a class(which is the model).

Simple controller tests with rspec rails4

I was looking into some rspec testing lately and I was wondering how to properly test controllers. My controller is fairly simple so it shouldn't be something too hard:
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
def index
#q = User.search(params[:q])
#users = #q.result(distinct: true)
#q.build_condition if #q.conditions.empty?
#q.build_sort if #q.sorts.empty?
end
# GET /users/1
def show
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
end
def archive
#q = User.search(params[:q])
#users = #q.result(distinct: true)
#q.build_condition if #q.conditions.empty?
#q.build_sort if #q.sorts.empty?
end
# POST /users
def create
#user = User.new(user_params)
if #user.save
redirect_to users_path, notice: 'Student was successfully added.'
else
render action: 'new'
end
end
# PATCH/PUT /users/1
def update
if #user.update(user_params)
redirect_to #user, notice: 'Student information was successfully updated.'
else
render action: 'edit'
end
end
# DELETE /users/1
def destroy
#user.destroy
redirect_to users_url, notice: 'Student information was successfully deleted.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def user_params
params.require(:user).permit(:firstName, :lastName, :email, :dateOfBirth, :notes, :sex, :archive, :category => [])
end
end
So far I have written 2-3 tests but I am not sure if they even do anything:
describe 'GET #index' do
it "displays all users" do
get :index
response.should be_redirect
end
end
describe 'GET #new' do
it "creates a new user" do
get :new
response.should be_redirect
end
end
I tried doing the same for edit and show but they didn't work and I am not sure why (because as I said, I don't know what I am doing).
Could anyone give me a few test examples for these methods or could redirect me to an rspec guide for rails4?
Are you expecting the controller #index action to redirect? Because that wouldn't be typical.
I would
describe 'GET #index' do
get 'index'
it {expect(response).to be_success)}
end
This line...
it "displays all users" do
in a controller spec makes me wonder if your confusing controller and request specs. I did this when I first got running with testing. "Displaying all users" sounds like a request spec to me. Testing if a page redirects or response status codes is more akin to controller specs.
I found http://betterspecs.org/ to be a really helpful resource in understanding testing better.
RE: WHAT to test
This worked for me but results may vary.
Controller Specs - Don't test controllers
Controllers should be skinny so you're just testing whether Rails is working. e.g. an index action may contain #users = User.all or similar and very little else. What is there to test there? Nothing. If you have lots of code in your controller actions then it probably shouldn't be there. Move it out to the models. Remember: Fat models, skinny controllers. This is an example of how testing creates better code. I have very few controller specs and I think nearly all of them are double checking authorisation to pages. I only use them where there's code in the controller. Here's an example:
context "Non admin signed in" do
before(:each) do
sign_in user
controller.stub!(:current_user).and_return(user)
end
it {subject.current_user.should_not be_nil}
it "deny non admin access to index" do
sign_in user
get 'index'
expect(response).to render_template("pages/access_denied")
end
end
Request Specs Test what you would test in a browser (20% of tests)
Imagine that you weren't doing RSpec testing. If you're like me then this is not too hard to imagine. How would you test the thing you want to build? Chances are that the first thing you'd do is load up a browser and see if something is on the page that you were expecting. That IS a request spec. It's that simple. Request specs are the automated ways of loading up a browser, clicking on a few buttons and checking what happened. Whatever it is your checking in the browser... check that same thing using Capybara. If it has Javascript on the page then you'll need Webkit or Selenium on top of Capybara to push the buttons as you would. With selenium you actually see the browser window pop up on the desktop as if a mysterious gremlin had taken control of your keyboard. Don't test anything in a request spec that you wouldn't be testing manually in a browser. That means don't check the state of other models in the database. Request specs are what the user can see. If you can't see it, don't test it.
Model specs - Test what you would test in the console (80% of tests)
Before I became a good TDD/BDD boy I found I spent a lot of time loading up irb or console and making models and doing X to see if Y would happen. Automate that thing. That's a model spec. When your request spec fails (which it should at first if it's doing anything useful) then drop down into the model spec. A failing request spec might be:
it {expect(page.find('#login_box')).to have_content 'Logged in as Kevin Monk'}
from
no method full_name for instance of User
And if you weren't a TDD good boy you might load up the console and find what was happening with the full_name method.
> rails console
$> kevin = User.find(1)
$> kevin.full_name
And then visually check that you get the full name baack but this should be done as a model spec.
I hope that helps. A problem I've had with a lot of books on testing is that the authors tend to be such experts and therefore don't appreciate that us mortals really need to understand the basic premise of what it is your supposed to be testing.
you have a typo in your spec code , you have to change respone, for response
I think that´s the problem
you can find more information in about test controllers in
https://www.relishapp.com/rspec/rspec-rails/docs/controller-specs
regards

Error Testing Create Action

I am trying to use factory girl to test my create action in rails. I keep getting:
"Event.count" didn't change by 1.
<2> expected but was
<1>.
When I run my tests. I don't believe I've done anything to change my tests or the controller:
My test looks like this:
test "should get create" do
assert_difference('Event.count') do
post :create, FactoryGirl.attributes_for(:event)
end
assert_not_nil assigns(:event)
assert_response :success
end
I've also tried using FactoryGirl.build(:event) as well. I've used that previously, and I think that is the right the method to be using from what I've read.
Here is my factory:
factory :event do
name 'First Event'
street '123 street'
city 'Chicago'
state 'IL'
date '1/12/2012'
end
Here is my controller action
def create
#event = Event.new(params[:event])
#event.save
end
Any idea what I'm doing wrong?
In order to scope out your error, try throwing a debugger in your action :
def create
#event = Event.new(params[:event])
debugger
#event.save
end
See if you manally resolve an #event.save and what errors might prevent that. :D

Resources