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.

Published by Avdi Grimm

9 Comments

  1. I think  subject.on_we_get_signal(&amp;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?

    Reply
  2. 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.

    Reply
  3. 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?

    Reply
  4. 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!

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *