The wording on the Message Chains page is very careful to warn that receive_message_chain
makes it painlessly easy to break the Law of Demeter. They even go on to explain that double and instance double offer a better way of testing code with these chained method patterns.
double
and receive
in place of receive_message_chain
The tests in question look like this
describe Game do subject(:game) { Game.new() } describe '#done' do context 'the game fairy says there is a tie' do it 'returns false' do allow(GameFairyGateway).to receive_message_chain(:get_fairy, :proclamation) { false } expect(game.done?).to be false end end context 'the game fairy says the player_1 is the winner' do it 'returns true' do allow(GameFairyGateway).to receive_message_chain(:get_fairy, :proclamation) { true } expect(game.done?).to be true end end end end
and they are for the following code
class GrumpyGameFairy < StandardError def initialize(msg="Outlook is cloudy, try again later") super end end class GameFairy def proclamation if mystic_visions == 'The game is in a stalemate!' false elsif mystic_visions == 'The game is won!' true else raise GrumpyGameFairy end end private def mystic_visions "Leave me alone!" end end class GameFairyGateway def self.get_fairy game_fairy = GameFairy.new() end end class Game def done? return GameFairyGateway.get_fairy.proclamation end end
Again, we want to avoid using the receive_message_chain
helper method. In its place we want to use a double for our GameFairy
. To that end we'll add game_fairy
as a helper methods via let.
describe '#done' do let(:game_fairy) { double }
This gives us a double to use in place of the real game fairy, but we still need to stub out the methods being used by the Game
class. We'll do this with allow
and receive
. We'll put those stubs into a before block in the root decribe so that both contexts can use it, and we'll add another helper method to dictate what value should be returned by proclamation
.
before do allow(game_fairy).to receive(:proclamation).and_return( proclamation_output ) allow(GameFairyGateway).to receive(:get_fairy).and_return(game_fairy) end
Then we change our test like so.
context 'the game fairy says there is a tie' do let(:proclamation_output) { false } it 'returns false' do expect(game.done?).to be false end end
Note that we've removed the receive_message_chain
stubbing, and we've added the proclamation_output
helper declaration.
This way might be a little less natural, but it's the recommendedation made by the RSpec documentation. You may also want to check your code for tight coupling of objects if you're using receive_message_chain
often.
You can find this code on my repo.
You can watch me converting an example on YouTube
Thanks for reading!
Back