Much Respect for RSpec, Part 2

Malina Tran
Tech and the City
Published in
3 min readJun 27, 2016

One of the key challenges to testing is figuring out precisely how to test with RSpec expectations, mocks, and stubs. Learning how testing works and the syntax can take awhile, so I thought I’d jot down examples for reference. Having a better grasp for how to test has definitely helped expedite test-driven programming.

  1. Testing within loops and conditions

Trying to run a test within while or until loops will lead to the test being caught in an infinite loop. The way to solve this is to use Rspec’s “allow” method, which provides a way to stub methods and force specific values to be returned. In order to enter the loop, the condition must evaluate to true, and on the second iteration, it must evaluate to false.

See below for an example method, `declare_outcome`, which has a nested conditional statement. This method returns a string if the game is over and it is a draw (no winners).

def declare_outcome 
if is_game_over?
if draw?
return “draw”
elsif win?
declare_winner
end
end
end

Here is the corresponding unit test. Here, I am setting up the test to enter and test that “draw” will be returned. In order to do this, we need to stub certain values. We need to `allow` the class — in this case, `Game` — to `receive` a method and `return` true or false. Since `is_game_over?` being true will allow us to enter the first nested statement, we need to explicitly state `true` as the first parameter. The second parameter is `false` which will make the loop’s condition evaluate to false, thus stopping its iteration. Since it is a nested conditional statement, the process is similar for `draw?`. The last line is setting the expectation for the return value of calling the method under these conditions that we have set up.

context “#declare_outcome” do 
it "returns information about draw" do
allow(game).to receive(:is_game_over?).and_return(true, false)
allow(game).to receive(:draw?).and_return(true, false)
expect(game.declare_outcome).to eq("draw")
end
end

2. Testing with instance variables

This is useful in the instance (hah!) that your instance variables are not available outside of the class, and you want to keep it that way. There are ways to get and set them in your unit tests. Take a look at this pretty straightforward method, where we’re just constructing a new board and passing in an instance variable, `@size`. We construct a new game and pass in the board as an argument.

def initialize 
board = Board.new(@size)
game = Game.new(board)
end

Let’s test that board has been properly created a board with a size of our choosing. We can compare the input with the output; that is, the `@size` value that has been passed into board with the size of the board that now exists in Game. You’ll notice that I’ve used `instance_variable_set` as a way to stub the value of `@size` to be 5. After calling the method, we should expect the output is equal to 5. Lo and behold, the test passes!

context “#initialize” do 
it “sets the board size” do
user_interface.instance_variable_set(:@size, 5)
user_interface.initialize
expect(game.board.size).to eq(5)
end
end

Stay tuned for more lessons learned with RSpec and testing!

--

--