Search code examples
rubytestingrspeccallblock

RSpec -- test if block called with block defined in before


I recently asked how to test in RSpec if a block was called and the answers to that question seem to work in a simple case. The problem is when the initialization with the block is more complex. Then it is done in before and reused by a number of different tests in the context, among them the one testing if the block was evaluated. See the example:

context "the node definition using block of code" do
  before do
     @n=node do
        # this block should be called
     end
     # some more complex setup concerning @n
  end

  it "should call the block" do
     # how to test it?
  end

  # here a bunch of other tests using @n

end

In this case the solution with side effect changing value of a local variable does not work. Raising an exception from the block is useless since the whole statement must be properly evaluated to be used by the other tests.

Obviously I could do the tests separately, but it seems to stink, since I must copy-paste the initialization part and since the was-the-block-called test inherently belongs to this very context.

How to test if the block was evaluated in such a case?


Explanation for question asked by @zetetic below.

The context is that I'm implementing a kind of DSL, with nodes defined by their parameters and blocks of code (that can define something else in the scope of node). Since the things defined by the node's block can be pretty generic, at least for the first attempt I just need to be sure the block is evaluated and that what a user provides there will be considered. For now does not matter what it is.

Probably I should refactor my tests now and using mocks make them test behaviors rather then implementation. However it will be a little bit tricky, for the sake of some mixins and dynamic handling of messages sent to objects. For now the cincept of such tests is a little bit fuzzy in my head ;-)

Anyway your answers and comments helped me to better understand how RSpec works and explained why what I'm trying to do looks as if it did not fit to the RSpec.


Solution

  • Try something like this (untested by me):

    context "the node definition using block of code" do
      let(:node){
        node = Node.new "arg1", "arg2", node_block
        # more complex stuff here
        node
      }
      context "checking the block is called" do
        let(:node_block) {
          double = double("node_block")
          double.should_receive("some kind of arg").and_return("something")
          # this will now cause a fail if it isn't called
          double
        }
        it "should call the block" do
          node.blah()
        end
      end
    
      let(:node_block) {
        # some real code
      }
    
      subject { node.blah() }
      it { should == 2 }
      # ...
    end
    

    So that's a very shaky piece of code (you'll have to fill in the gaps as you didn't give very much to go on, and let is obviously a lambda too, which could mean you've got to play around with it a bit) that uses let and a double to check it's called, and avoids using before, which is really for side effects not setting up variables for use in the specs.

    @zetetic makes a very insightful comment that you're not testing behaviour here. I'm not against using rspec for doing more unit test style stuff (guidelines are made to be broken), but you might ask how later tests will pass when using a real block of code if that block isn't being called? In a way, I'm not even sure you need to check the block is called, but only you know.