receive_message_chain Alternative

Created: 2020-06-07, Last Updated: 2023-02-04

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.

Using 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.

Conclusion

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