Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialAndrew Smith
14,330 PointsSilent Failure of a Controller Update Action Test in RSpec
I'm working on a team to develop an open-source chess game in Rails, and trying to test for a chess piece's ability to have its position on the board updated upon making a move. For some reason, an update request in RSpec seems to be failing. Neither myself nor my teammates know what's causing the seemingly silent failure in the update request, so I'd be thankful if anybody could shed some light on what's going on. The pieces controller itself works when actually interacting with the chess board (legal moves for each chess piece have been successfully implemented), so this must be a problem with the way I've written the test.
Failures:
1) PiecesController Action: pieces#update should create a move when a move is valid
Failure/Error: expect(piece.y_position).to eq 5
expected: 5
got: 7
(compared using ==)
# ./spec/controllers/pieces_controller_spec.rb:21:in `block (3 levels) in <top (required)>'
# The Failing Test:
RSpec.describe PiecesController, type: :controller do
describe "Action: pieces#update" do
it "should create a move when a move is valid" do
user = FactoryGirl.create(:user)
sign_in user
game = FactoryGirl.create(:game, :white_player_id => user.id)
# Test a white pawn's movement on its first turn:
piece = FactoryGirl.create(:piece, :game => game, :player_id => user.id)
move = FactoryGirl.create(:move, :piece => piece)
if piece.valid_move?({ :x_position => piece.x_position, :y_position => 5, :type => piece.type, :captured => piece.captured })
# Move the pawn 2 vertical spaces on its first turn, and record the move:
# *** PROBLEM: The piece's y-position is not being updated. ***
patch :update, :id => piece.id, :piece => { :y_position => 5 }, :format => :js
piece.reload
# *** The expectation below fails. ***
expect(piece.y_position).to eq 5
else
# Safety condition to ensure the test fails visibly when the move is invalid.
expect(move.move_count).to eq "Invalid move!"
end
end
end
end
# Associations:
class User < ActiveRecord::Base
...
has_and_belongs_to_many :games
has_many :pieces, foreign_key: :player_id
...
end
class Game < ActiveRecord::Base
has_many :pieces
has_many :moves, through: :pieces
belongs_to :white_player, class_name: 'User', foreign_key: :white_player_id
belongs_to :black_player, class_name: 'User', foreign_key: :black_player_id
...
end
class Piece < ActiveRecord::Base
belongs_to :game
has_many :moves
def valid_move?(params)
...
end
...
end
# Factories:
FactoryGirl.define do
factory :user do
...
end
factory :game do
association :white_player, factory: :user
association :black_player, factory: :user
turn 1
end
factory :piece do
association :game
type "Pawn"
color "white"
captured false
x_position 1
y_position 7
end
factory :move do
association :piece
...
end
end
# Pieces Controller:
class PiecesController < ApplicationController
before_action :authenticate_user!
before_action :require_authorized_for_current_game, only: [:update]
before_action :require_authorized_for_current_piece, only: [:update]
before_action :valid?, only: [:update]
def update
current_piece.capture_piece(piece_params)
Piece.find_by_id(params[:id]).update_attributes(piece_params) # Do not use current_piece here solely for pawn promotion
current_game.next_turn
# Send a message back to the JS after the update (after the data object is defined in the AJAX request) to confirm successful update or an error:
respond_to do |format|
format.js { render json: {success: true, status: :success} }
end
end
private
def current_piece
@current_piece ||= Piece.find_by_id(params[:id])
end
def require_authorized_for_current_piece
if current_piece.player_id != current_user.id
render text: 'Unauthorized', status: :unauthorized
end
end
def piece_params
params.require(:piece).permit(:x_position, :y_position, :type, :captured)
end
def current_game
@current_game ||= current_piece.game
end
def require_authorized_for_current_game
if current_game.white_player != current_user && current_game.black_player != current_user
render text: 'Unauthorized', status: :unauthorized
end
end
def valid?
if !current_game.your_turn?(current_piece) || !current_piece.valid_move?(piece_params)
render text: 'Unauthorized', status: :unauthorized
end
end
end
1 Answer
Steve Hunter
57,712 PointsHi Andrew,
I'm not clear on this, I'm afraid. You've got a piece that's at x: 1 y: 7 and you move it, expecting the y position to become 5. But you still get 7. That right?
After the move, you've called reload
; does that want to be save
instead? Maybe reload
is bringing the piece back in from factory at x: 1 y:7? Alternatively, I know that in the Rails console, you call reload!
with the exclamation mark. I don't know if that's the same in Rspec.
Just a couple of points that may, or may not, help! Sorry I can't be of much more use.
Steve.
Andrew Smith
14,330 PointsAndrew Smith
14,330 PointsThanks for taking a look at this code. The problem was occurring in the before_action :valid? line in the Pieces Controller, where !current_piece.valid_move?(piece_params) was returning true, causing the test to fail silently.
Calling reload works fine after this was addressed.
Steve Hunter
57,712 PointsSteve Hunter
57,712 PointsGood work. Glad you got it sorted.