How to test for yield self

69 views
Skip to first unread message

Maurizio De Santis

unread,
Nov 15, 2013, 5:36:20 AM11/15/13
to rs...@googlegroups.com
Hello,

I'm using rspec 3.0.0.beta1. I'm trying to test for yield self. Example:

class Test
  def initialize
    yield self if block_given?

  end
end

I can't figure out how to test it. This is the closest successful test I wrote:

 
describe Test do
  context 'giving a block with one argument' do
    it 'yields itself'
      expect { |b| described_class.new &b }.to yield_with_args described_class
    end
  end
end

But it tests only the yield argument type, not the object identity.

This is a failing test that goes close to the wanted result:


describe Test do
  context 'giving a block with one argument' do
    it 'yields itself'
      instance = nil
      expect { |b|
        instance = described_class.new &b
      }.to yield_with_args instance
    end
  end
end

Indeed it fails, since that at the time that the last instance occurrence is evaluated its value is nil, so it doesn't match with instance value at the block evaluation time.

Do you have some ideas about?

Thank you

Myron Marston

unread,
Nov 15, 2013, 10:59:02 AM11/15/13
to rs...@googlegroups.com
Thanks for trying out the 3.0 beta :).

The yield matchers try to match the args with both `===` (for case equality) and `==` (for exact equality) and in 1.9+, lambda's respond to `===` by invoking their logic.  So while it's a bit of a hack, this works:

it 'works using a lambda' do
  instance = nil
  equals_the_instance = ->(arg) { arg.equal?(instance) }
  expect { |b|
    instance = Test.new(&b)
  }.to yield_with_args(equals_the_instance)
end

It works because passing the lambda causes it to be lazily evaluated.

That said, I don't think the `yield_with_args` matcher offers you much advantage in this case.  It's simpler to do this:

it 'works without the matcher' do
  yielded = []
  instance = Test.new { |a| yielded << a }
  expect(yielded).to eq([instance])
end

HTH,
Myron

Aaron Kromer

unread,
Nov 15, 2013, 12:52:19 PM11/15/13
to rs...@googlegroups.com

I believe the expectation matchers also now support === in RSpec 3. So you could write it as:

it "works using expectation matcher" do
  expect{ |b| Foo.new(&b) }.to yield_with_args instance_of Foo
end


--
You received this message because you are subscribed to the Google Groups "rspec" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rspec+un...@googlegroups.com.
To post to this group, send email to rs...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/rspec/2fea7ccd-d18b-48c4-a47b-aa1baa7c307f%40googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.

Aaron Kromer

unread,
Nov 15, 2013, 12:53:02 PM11/15/13
to rs...@googlegroups.com
Though that's not as explicit as Myron's example. I'd probably go with his.

Myron Marston

unread,
Nov 15, 2013, 1:13:27 PM11/15/13
to rs...@googlegroups.com
I believe the expectation matchers also now support === in RSpec 3.

Not yet.  Still on the list, but we haven't implemented it yet.

That said, some matchers (such as the `yield` matchers) have supported `===` for a long time.  In fact, that's how Maurizio's original example worked:

expect { |b| described_class.new &b }.to yield_with_args described_class

Class#=== returns true when given an instance of the class.  No `instance_of` needed.

However, Maurizio specifically said:

But it tests only the yield argument type, not the object identity.

Myron

Aaron Kromer

unread,
Nov 15, 2013, 1:15:59 PM11/15/13
to rs...@googlegroups.com

Ah good, thanks for the correction

Reply all
Reply to author
Forward
0 new messages