Specifically, what is the difference between stubs and mocks? What does it mean if we are stubbing a method vs. mocking an object? Both are related to creating some fake data and expectations regarding how our code show behave. There is also some confusion regarding the terminology.
According to RSpec docs, a method stub is
"an instruction to an object (real or test double) to return a known value in response to a message". A message expectation is "an expectation that an object should receive a specific message during the course of a code example".The main difference lies in the message expectation part. Both stubs and mocks operate with test doubles, which are just like stunt doubles standing in for some celebrity. However, when a stub creates a test double for a method, it does not create an expectation that the method will be called. In other words, it just returns a specified result for the method without calling an actual method. On the opposite, a mock does create an expectation that the method will be called. As a result, stubs do not fail a test (because they do not trigger an actual method), but mocks can fail (if a triggered method does not return expected results or if a method is not called).
Here is a simplified and a bit artificial example from Connect Four game. Let's say we want to check whether a given color was placed in a given location at a board. Note that the
set_color_to function is called twice on purpose:def set_color_to(color)
@color = color
end
def set_colors_at(x,y)
@matrix[x][y] = set_color_to("blue") unless !@matrix[x][y].nil?
end
def iam_a_weird_function
set_color_to("red")
x,y = 1,2
set_colors_at(x,y)
end
def get_color_at(x,y)
@matrix[x][y]
end
And the tests look like so:
let(:board) { Board.new }
describe "stubs vs mocks" do
it "uses a stub" do
allow(board).to receive(:set_color_to).and_return("green")
board.iam_a_weird_function
expect(board.get_color_at(1,2)).to eql "green"
end
it "uses a mock" do
# expect(board).to receive(:set_color_to).and_return("green")
expect(board).to receive(:set_color_to).and_return("green").exactly(2).times
board.iam_a_weird_function
expect(board.get_color_at(1,2)).to eql "green"
end
end
The first test passes because we stub the
set_color_to function, which returns a predetermined value of green. Even though the set_color_to function is called twice in the actual code, it doesn't matter in the test because a stub doesn't require a method to be actually called.The second test fails at line 12 and complains that it expected to receive a message once with any arguments; instead, a message was received twice. This is happening because a mock has an expectation that the
set_color_to function will be called. Line 13 fixes this by adding exactly(2).times option, which specifies how many times we expect to receive a message. This example shows functional differences between stubs and mocks; what is the conceptual difference? Testing is based on the idea that each test should test a single behavior; using stubs and mocks helps to achieve this. Thus, a stub could be used to check if a correct view is being rendered in an application, but it wouldn't check if an appropriate method gets called. The latter should be tested in a separate function.
No comments :
Post a Comment