I'm learning Rails and how to test using RSpec. I need help completing an RSpec controller test of the PUT update action. The controller directs a nested resource. The nested route and related program code work as expected in a web browser. I only need help writing the RSpec test for the PUT update action. I have passing tests of the other actions performed by the controller.
I know that the path and both parent and child ids must be passed to the put method, but I'm confused about the following: (1) how to properly set up the PUT update test using FactoryGirl objects, and (2) the proper RSpec syntax to pass the path and ids into the PUT method.
The relevant routes from rake routes:
PATCH /members/:member_id/cardio_exercises/:id(.:format) cardio_exercises#update
PUT /members/:member_id/cardio_exercises/:id(.:format) cardio_exercises#update
routes.rb contains: resources :members do resources :cardio_exercises end
members.rb contains: has_many :cardio_exercises, :dependent => :destroy
cardio_exercises.rb contains: belongs_to :member
The relevant factories:
FactoryGirl.define do
factory :cardio_exercise do
title "My cardio exercise"
duration 30
calories_burned 300
date "2014-11-15"
association :member
end
end
FactoryGirl.define do
factory :member do
first_name {Faker::Name.first_name}
last_name {Faker::Name.last_name}
age 21
height 75
weight 195
goal "fffff" * 5
start_date "2014-11-15"
end
end
The cardio_exercises_controller.rb
class CardioExercisesController < ApplicationController
# :get_member is defined in the private method at the bottom of this file,
# and takes the member_id provided by the routing and
#converts it to a @member object.
before_action :get_member
# GET member/1/cardio_exercises
# GET member/1/cardio_exercises.json
def index
@cardio_exercises = @member.cardio_exercises
end
# GET member/1/cardio_exercises/1
# GET member/1/cardio_exercises/1.json
def show
cardio_exercise = @member.cardio_exercises.find(params[:id])
end
# GET member/1/cardio_exercises/new
def new
@member = Member.find(params[:member_id])
@cardio_exercise = @member.cardio_exercises.build
end
# GET member/1/cardio_exercises/1/edit
def edit
@cardio_exercise = @member.cardio_exercises.find(params[:id])
end
# POST member/1/cardio_exercises
# POST member/1/cardio_exercises.json
def create
@cardio_exercise = @member.cardio_exercises.build(cardio_exercise_params)
if @cardio_exercise.save
flash[:success] = "Cardio exercise was successfully created."
redirect_to member_cardio_exercises_path(@member)
else
render 'new'
end
end
# PATCH/PUT member/1/cardio_exercises/1
# PATCH/PUT member/1/cardio_exercises/1.json
def update
@cardio_exercise = @member.cardio_exercises.find(params[:id])
if @cardio_exercise.update(cardio_exercise_params)
flash[:success] = "Cardio exercise was successfully updated."
redirect_to member_cardio_exercises_path(@member)
else
render 'edit'
end
end
# DELETE member/1/cardio_exercises/1
# DELETE member/1/cardio_exercises/1.json
def destroy
@cardio_exercise = @member.cardio_exercises.find(params[:id])
@cardio_exercise.destroy
respond_to do |format|
format.html { redirect_to (member_cardio_exercises_path(@member)), notice: 'Cardio exercise was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# The get_member action converts the member_id given by the routing
# into an @member object, for use here and in the view.
def get_member
@member = Member.find(params[:member_id])
end
def cardio_exercise_params
params.require(:cardio_exercise).permit(:title, :duration, :calories_burned, :date, :member_id)
end
end
The cardio_exercises_controller_spec.rb and my attempt to set up several tests of the PUT update action:
require 'rails_helper'
RSpec.describe CardioExercisesController, :type => :controller do
before :each do
@member = FactoryGirl.create(:member)
@cardio_exercise = FactoryGirl.create(:cardio_exercise, member: @member)
@cardio_exercise_attributes = FactoryGirl.attributes_for(:cardio_exercise, :member_id => @member)
@cardio_exercise_update_attributes = FactoryGirl.attributes_for(:cardio_exercise, :title => "Your Cardio Exercise", :member_id => @member)
end
describe "GET index" do
it "assigns all cardio_exercises as @member.cardio_exercises" do
get :index, { :member_id => @member }
expect(assigns(:cardio_exercises)).to eq(@member.reload.cardio_exercises)
end
end
describe "GET show" do
it "assigns the requested cardio_exercise as @cardio_exercise" do
get :show, { :member_id => @member, :id => @cardio_exercise }
expect(assigns(:cardio_exercise)).to eq(@cardio_exercise)
end
end
describe "GET new" do
it "assigns a new cardio_exercise as @member.cardio_exercise" do
get :new, { :member_id => @member }
expect(assigns(:cardio_exercise)).to be_a_new(CardioExercise)
end
end
describe "GET edit" do
it "assigns the requested cardio_exercise as @member.cardio_exercise" do
get :edit, { :member_id => @member, :id => @cardio_exercise }
expect(assigns(:cardio_exercise)).to eq(@cardio_exercise)
end
end
describe "POST create" do
describe "with valid params" do
it "creates a new CardioExercise" do
expect {
post :create, { :member_id => @member, :cardio_exercise => @cardio_exercise_attributes }
}.to change(CardioExercise, :count).by(1)
end
it "assigns a newly created cardio_exercise as @cardio_exercise" do
post :create, { :member_id => @member, :cardio_exercise => @cardio_exercise_attributes }
expect(assigns(:cardio_exercise)).to be_a(CardioExercise)
expect(assigns(:cardio_exercise)).to be_persisted
end
it "redirects to the created cardio_exercise" do
post :create, { :member_id => @member, :cardio_exercise => @cardio_exercise_attributes }
expect(response).to redirect_to(member_cardio_exercises_path(@member))
end
end
end
describe "PUT update" do
describe "with valid params" do
it "updates the requested cardio_exercise" do
put :update, { :member_id => @member, :id => @cardio_exercise_update_attributes }
@member.cardio_exercise.reload
put :update, { :member_id => @member, :id => @cardio_exercise_update_attributes}
end
it "assigns the requested cardio_exercise as @member.cardio_exercise" do
put :update, { :member_id => @member, :cardio_exercise => @cardio_exercise_update_attributes = FactoryGirl.attributes_for(:cardio_exercise, :title => "Your Cardio Exercise", :member_id => @member)}
expect(assigns(:cardio_exercises)).to eq(@member.cardio_exercises)
end
it "redirects to the cardio_exercise" do
put :update, { :member_id => @member, :id => @cardio_exercise }
expect(response).to redirect_to(@member.cardio_exercise)
end
end
describe "with invalid params" do
xit "assigns the cardio_exercise as @member.cardio_exercise" do
end
xit "re-renders the 'edit' template" do
expect(response).to render_template("edit")
end
end
end
describe "DELETE destroy" do
it "destroys the requested cardio_exercise" do
expect {
delete :destroy, { :member_id => @member, :id => @cardio_exercise }
}.to change(CardioExercise, :count).by(-1)
end
it "redirects to the cardio_exercises list" do
delete :destroy, { :member_id => @member, :id => @cardio_exercise }
expect(response).to redirect_to(member_cardio_exercises_url)
end
end
end
Please review my test set-up and the PUT update tests. Specifically, is my set-up the correct way to test the PUT update action?
How do I change the set-up and/or tests of the controller's update action so that:
(1) my tests match my routes for the update action?
(2) I pass the necessary parent and child ids in the correct RSpec syntax so records can be found?
I appreciate any help!
You must pass the member_id
, the cardio_exercise's id
, AND the cardio_exercise
parameters to update:
put :update, {
:member_id => @member,
:id => @cardio_exercise.id,
:cardio_exercise => @cardio_exercise_update_attributes
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With