I'm a beginner working my way through Agile Web Development with Rails 4 where we (readers) build a shopping-cart style demo app. So, far the main REST resources are 1) products, 2) carts and 3) line items. This is my second demo/sample app on my path to learning Rails and everything seems straight-forward so far, except I'm consistently getting an error in my tests for the update action in the line items controller. I'm not sure whether it's an error related to the test itself, the actual controller action, and/or my fixtures settings. (I'm using the "built-in" MiniTest with fixtures for the first time; previous sample app I did used RSpec and FactoryGirl.)
The error (not failure) I'm getting looks like this:
Finished tests in 0.936959s, 26.6821 tests/s, 56.5660 assertions/s.
1) Error:
LineItemsControllerTest#test_should_update_line_item:
ActionController::ActionControllerError: Cannot redirect to nil!
app/controllers/line_items_controller.rb:54:in `block (2 levels) in update'
app/controllers/line_items_controller.rb:52:in `update'
test/controllers/line_items_controller_test.rb:38:in `block in <class:LineItemsControllerTest>'
25 tests, 53 assertions, 0 failures, 1 errors, 0 skips
I'm unclear why I'm redirecting to nil. Is the path bad? Or is it the object at the end of the path? And/or "other"? I don't readily see anything in the controller itself that looks like a problem (regardless, I've included the code below), so I suspect it has something to do with the test file or fixtures settings. I've tried filling in various 'dummy' (e.g., product_id: 55
) and 'referenced' (e.g., product_id: products(:ruby).id
) values in the line_items.yml fixtures with no success.
I've also tried different things within the controller test setup, such as referencing specific fixture IDs in other fixture types (products and carts) that line items belong to (same idea as above). I tried editing the update test in a number of ways as well, including trying a redirect to the line item's cart instead of the line_items_path, such as:
assert_redirected_to cart_path(assigns(:line_item).cart)
...instead of...
assert_redirected_to line_item_path(assigns(:line_item))
Also tried variations of defining :line_item, which I take to be the updated object, e.g.:
patch :update, id: @line_item, line_item: { product_id: products(:ruby).id }
...instead of...
patch :update, id: @line_item, line_item: { product_id: @line_item.product_id }
What I believe to be relevant code included below. Any help very much appreciated!
line_items_controller.rb
class LineItemsController < ApplicationController
include CurrentCart
before_action :set_cart, only: [:create]
before_action :set_line_item, only: [:show, :edit, :update, :destroy]
# GET /line_items
# GET /line_items.json
def index
@line_items = LineItem.all
end
# GET /line_items/1
# GET /line_items/1.json
def show
end
# GET /line_items/new
def new
@line_item = LineItem.new
end
# GET /line_items/1/edit
def edit
end
# POST /line_items
# POST /line_items.json
def create
product = Product.find(params[:product_id])
@line_item = @cart.add_product(product.id)
respond_to do |format|
if @line_item.save
format.html { redirect_to @line_item.cart,
notice: 'Line item was successfully created.' }
format.json { render action: 'show',
status: :created, location: @line_item }
else
format.html { render action: 'new' }
format.json { render json: @line_item.errors,
status: :unprocessable_entity }
end
end
session[:count] = nil
end
# PATCH/PUT /line_items/1
# PATCH/PUT /line_items/1.json
def update
respond_to do |format|
if @line_item.update(line_item_params)
format.html { redirect_to @line_item.cart,
notice: 'Line item was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: @line_item.errors, status: :unprocessable_entity }
end
end
end
# DELETE /line_items/1
# DELETE /line_items/1.json
def destroy
@line_item.destroy
respond_to do |format|
format.html { redirect_to line_items_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_line_item
@line_item = LineItem.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def line_item_params
params.require(:line_item).permit(:product_id)
end
end
line_items_controller_test.rb
require 'test_helper'
class LineItemsControllerTest < ActionController::TestCase
setup do
@line_item = line_items(:one)
end
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:line_items)
end
test "should get new" do
get :new
assert_response :success
end
test "should create line_item" do
assert_difference('LineItem.count') do
post :create, product_id: products(:ruby).id
end
assert_redirected_to cart_path(assigns(:line_item).cart)
end
test "should show line_item" do
get :show, id: @line_item
assert_response :success
end
test "should get edit" do
get :edit, id: @line_item
assert_response :success
end
test "should update line_item" do
patch :update, id: @line_item, line_item: { product_id: @line_item.product_id }
assert_redirected_to line_item_path(assigns(:line_item))
end
test "should destroy line_item" do
assert_difference('LineItem.count', -1) do
delete :destroy, id: @line_item
end
assert_redirected_to line_items_path
end
end
line_items.yml
one:
product_id: #Trying values here doesn't seem to help.
cart_id: #Trying values here doesn't seem to help.
two:
product_id:
cart_id:
products.yml
one:
title: MyString
description: MyText
image_url: MyString
price: 9.99
two:
title: MyString
description: MyText
image_url: MyString
price: 9.99
ruby:
title: Programming Ruby 1.9
description:
Ruby is the fastest growing and most exicting dynamic
language out there. If you need to get working programs
delivered fast, you should add Ruby to your toolbox.
price: 49.50
image_url: ruby.png
three:
title: MyString
description: MyText
image_url: MyString
price: 9.99
carts.yml
one: {}
# column: value
#
two: {}
# column: value