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 trial

Ruby

Andrew Smith
Andrew Smith
14,330 Points

Silent 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

Hi 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
Andrew Smith
14,330 Points

Thanks 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.

Good work. Glad you got it sorted. :+1: