Reputation: 3390
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
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
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