Search code examples
rubymethodsrspectddcall

RSPEC Calling Methods Error


Here's the method that's giving the error/falure:

def draw?(board)
    if full?(board) == true && won?(board) == false
        return true
    else
        return false
    end
end

The method does everything is supposed to do but this is the error that appears when I run the rspec file:

1) ./lib/tic_tac_toe.rb #draw? calls won? and full?                                                                                    
     Failure/Error: expect(self).to receive(:won?).with(board)                                                                           
       (#<RSpec::ExampleGroups::LibTicTacToeRb::Draw:0x00000002e300b0>).won?(["X", "O", "X", "O", "X", "X", "O", "X", "O"])              
           expected: 1 time with arguments: (["X", "O", "X", "O", "X", "X", "O", "X", "O"])                                              
           received: 0 times 

Here's the rspec test:

describe '#draw?' do

    it 'calls won? and full?' do
      board = ["X", "O", "X", "O", "X", "X", "O", "X", "O"]
      expect(self).to receive(:won?).with(board)
      expect(self).to receive(:full?).with(board)

      draw?(board)
    end

Essentially, I read: #draw? fails to call #full? and #won?

But when I look at my #draw? method, I do call them! as the "if" conditional!

So where am I going wrong?

Help is greatly appreciated!

I'm currently learning Ruby.


Solution

  • 1) Your entire draw? method can be simplified to one line: full?(board) && !won?(board)

    2) Your need your expect to be spying on an instance of the class, or the class itself if you're testing class methods. You're using self which will be RSpec itself, not the underlying class you're testing. Since I don't know the name of it, I'll assume it's called Game. Try something like this:

    describe '#draw?' do
      it 'calls draw? and full?' do
        board = ["X", "O", "X", "O", "X", "X", "O", "X", "O"]
        game = Game.new
        expect(game).to receive(:won?).with(board)
        expect(game).to receive(:full?).with(board)
        game.draw?
      end
    end
    

    3) Another issue here is that you're expecting both methods to be called, but won? won't be called if full? returns false. This is due to short circuiting. In situations where all statements must evaluate to true (or false), once one of them does not, the rest are not evaluated. Think of an example like: if user != nil && user.admin?. You want short-circuiting here so that the second statement does not get evaluated if the first isn't true. And in general it's an unnecessary use of resources to evaluate statements whose output doesn't matter.

    4) Lastly, in general you're best off testing the output of the function rather than the internals of how it works. Better off having a bunch of tests where you pass in data and then expect a result.