thefonso
thefonso

Reputation: 3390

how to write rspec tests for one method

I have this method as part of a larger class. I'm trying to write a test for it but I'm new to rspec and I'm kinda stumped...I can test 'drawgrid' if I comment out everything in the 9.times loop. but if I uncomment that code the current test fails. I need to test that the play method...runs the game. that it puts 'drawgrid'...runs the game sequence 9 times putting the 'drawgrid' after each turn. But I'm not sure how to do this. Any pointers are greatly appreciated.

Below is the play method and it's current spec

      def play
        #draw the board
        puts drawgrid

        #make a move
        turn = 0

        9.times do

          if turn.even?

            @player = @player_h.move_human("X", @board)

            @move = @player.to_sym
            @marker = @player_h.boardpiece

            does_move_exist(@move,@marker)
            is_a_human_win(@board)

          else

            @player = @player_c.move_computer("O", @board)

            @move = @player
            @marker = @player_c.boardpiece

            does_move_exist(@move,@marker)
            is_a_computer_win(@board)

          end

          puts drawgrid

          turn += 1
        end # 9.times ends
      end

current spec....

      describe 'play method' do
        it 'draws the game grid' do
          @player_human = Player.new('X')
          @player_computer = Player.new('O')
          @board = Board.new
          @game = Game.new(@player_human, @player_computer, @board)

          @game.should_receive(:puts).with("\na  | |  \n----------\nb  | |  \n----------\nc  | |  \n----------\n  1 2 3\n")

          @game.play
        end
      end
      describe '9.times' do
        it 'runs game sequence 9 times...once per board spot' do
          @player_human2 = Player.new('X')
          @player_computer2 = Player.new('O')
          @board2 = Board.new
          @game2 = Game.new(@player_human2, @player_computer2, @board2)

          turn = 0       
          9.times do
            if turn.even?
              @player_human2.should_receive(:puts).with("human move...")
              @player_human2.stub(:gets).and_return("b2")
            else
              @player_human2.should_receive(:puts).with("computer move...")
              @player_human2.stub(:gets).and_return("a1")
            end
            turn += 1
          end
        @game2.play
        end
      end

Upvotes: 1

Views: 913

Answers (2)

Kori John Roys
Kori John Roys

Reputation: 2661

I second what Dave says. Try to simplify. Basically, if you can simplify your play method, it will simplify your test. Right now play is concerned with the implementation details of each turn. Push those details down, and your test can become easier to write and more granular. I'm not the best, and I'm not super happy with this yet, but hopefully the code below pushes you in the right direction:

#play.rb

class Board
end

class Player
  def initialize(symbol)
    @symbol = symbol
  end

  def take_turn
  end
end

class Game
  def initialize(player1, player2, board)
    @player1, @player2, @board = player1, player2, board
  end 

  def play
    drawgrid

    (0...9).each do |turn|
      turn.even? ? @player1.take_turn : @player2.take_turn
      drawgrid
    end
  end

  def drawgrid
  end
end

And the test file:

#play_spec.rb
require './play.rb'

describe '#play' do
  before do
    @player1 = Player.new('X')
    @player2 = Player.new('O')
    @game = Game.new(@player1, @player2, Board.new)
  end

  it 'draws the game grid' do
    @game.should_receive(:drawgrid).at_least(:once)
    @game.play
  end

  it 'runs game sequence 9 times...once per board spot' do
    @player1.stub(take_turn: true)
    @player2.stub(take_turn: true)
    @player1.should_receive(:take_turn).exactly(5).times
    @player2.should_receive(:take_turn).exactly(4).times
    @game.play
  end
end

Upvotes: 3

Dave Giunta
Dave Giunta

Reputation: 81

In general, I feel like both your code and your test are trying to do too much in one method. The interesting bit about your play method isn't so much the 9 times as what happens inside of that loop. My first suggestion for refactoring that is to turn what's inside that loop into a method called "take_turn" or something similar.

Then you could write specs for what happens for a single turn. And, your spec for the play method would test that the take_turn method is called 9 times.

That's not to say that you couldn't keep your code the way that it is and write an effective test for it... you just can't be super surgical about what you're testing.

Hope that helps.

Upvotes: 3

Related Questions