Testing that a block is called

CapnKernul asks:

Hey Avdi. How would you test that a method’s provided block is called
in RSpec? Would you stub #to_proc (for &block) and mock #call?

Typically the way I test that a block is called goes something like this:

describe ZeroWing do
  let(:probe) { lambda{} }

  context "given a signal handler" do
    subject.on_we_get_signal(&probe)

    it "triggers the handler when signaled" do
      probe.should_receive(:call)
      subject.we_get_signal!
    end
  end
end

That’s for the simple case of just checking if the block is called. When I want to make assertions about the block arguments, I often switch to a style which records the yielded arguments and then makes assertions on them after the fact.

describe Array do
  subject { [1,2,3] }

  describe "#reverse_each" do
    it "yields the elements in backwards order" do
      yielded = []
      subject.reverse_each do |e| yielded << e end
      yielded.should eq([3,2,1])
    end
  end
end

I don’t know if this is the best way, but it’s the way I usually do it.

This entry was posted in Ruby and tagged , , . Bookmark the permalink.
  • http://twitter.com/myronmarston Myron Marston

    I think  `subject.on_we_get_signal(&probe)` needs to be in a `before(:each)` block–you can’t call it like that ddirectly in the “class body” of the example group.

    The mocking approach you’ve described here is interesting, I always use something closer to your second approach even for the simple case of just asserting the block was called:

    it ‘calls the given block’ do
    block_called = false
    subject.method_that_calls_block { block_called = true }
    block_called.should be_true
    end

    The mocking doesn’t gain anything over this approach IMHO, and it’s more fragile. If the method-under-test calls the block using #call, it’ll work fine, but what if it just uses a plain old `yield`?

    • http://twitter.com/banisterfiend banisterfiend

      +1 for `block_called = true`

  • http://twitter.com/peeja Peter Jaros

    What about something like `MockBlock` here: https://gist.github.com/1428875

    That doesn’t care whether the implementation actually uses `#call` or `yield`, and shows a readable technique for testing the order of the calls.  I find that a lot nicer to read than the old `block_called.should be_true` or `yielded.should ==` style.

    • http://avdi.org Avdi Grimm

      Nice!

  • http://twitter.com/chipcastle chipcastle

    Given the following code block:

    barcode = Barby::Code128B.new(data)
    File.open(“test.png”, ‘w’) do |f|
    f.write barcode.to_png(height: 100, margin: 5)
    end

    How would I test that code within the block was executed? Specifically the calls to f.write or barcode.to_png?

  • StuartCorbishley

    The first example is just great! I really don’t like creating superfluous instance variables to validate things. By adding on a .with(foo) I can keep my tests completely out of the caller/consumer space. Thanks!