I would like to use RSpec to ensure that my enumerable class is compatible with Ruby's visitor pattern:
# foo.rb
class Foo
  def initialize(enum)
    @enum = enum
  end
  include Enumerable
  def each(&block)
    @enum.each(&block)
  end
end
Here is my rspec file:
# spec/foo_spec.rb
require 'rspec' 
require './foo.rb' 
describe Foo do 
  let(:items) { [1, 2, 3] } 
  describe '#each' do 
    it 'calls the given block each time' do 
      block = proc { |x| x }
      block.should_receive(:call).exactly(items.size).times 
      Foo.new(items).each(&block) 
    end 
  end 
end
But surprisingly, my examples fail when run (with rspec v2.14.5):
# $ bundle exec rspec
Failures:
  1) Foo#each calls the given block each time
     Failure/Error: block.should_receive(:call).exactly(items.size).times
       (#<Proc:0x007fbabbdf3f90@/private/tmp/rspec-mystery/spec/foo_spec.rb:8>).call(any args)
           expected: 3 times with any arguments
           received: 0 times with any arguments
     # ./spec/foo_spec.rb:12:in `block (3 levels) in <top (required)>'
Finished in 0.00082 seconds
1 example, 1 failure
Failed examples:
rspec ./spec/foo_spec.rb:11 # Foo#each calls the given block each time
Even more surprising, the class itself behaves exactly as I expect when used via ruby/irb:
# $ irb -r ./foo.rb
1.9.3-p125 :002 > f = Foo.new [1, 2, 3]
 => #<Foo:0x007ffda4059f70 @enum=[1, 2, 3]> 
1.9.3-p125 :003 > f.each
 => #<Enumerator: [1, 2, 3]:each> 
1.9.3-p125 :004 > block = proc { |x| puts "OK: #{x}" }
 => #<Proc:0x007ffda483fcd0@(irb):4> 
1.9.3-p125 :005 > f.each &block
OK: 1
OK: 2
OK: 3
 => [1, 2, 3] 
Why doesn't RSpec notice that the "block" does in fact receive the "call" message three times?
Because, AFAICT, on MRI, it doesn't.
#each isn't provided by Enumerable, only by the classes that implement it, and in your test, you're using an Array.
Here's the source code (in C) from Array#each:
VALUE rb_ary_each(VALUE array)
{
    long i;
    volatile VALUE ary = array;
    RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length);
    for (i=0; i<RARRAY_LEN(ary); i++) {
        rb_yield(RARRAY_PTR(ary)[i]);
    }
    return ary;
}
From this, it looks like Array#each yields to the block rather than calling it explicitly.
UPDATE:
Your code & test fails on Rubinius & JRuby as well, so it looks like their standard libraries don't use call here either. As @mechanicalfish points out, you really only need to test that the iterator goes over the collection the correct number of times.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With