Access later-created variables on a receive block?

18 views
Skip to first unread message

Elad Ossadon

unread,
Jul 15, 2014, 4:53:16 PM7/15/14
to rs...@googlegroups.com
In the following case I don't have access to created_object until it's created, but I expect that somewhere inside SomeCreateService.create the method SomeProcessService.create would be called with the created object:


it "does something" do
  expect(SomeProcessService).to receive(:process) do |object|
    expect(object).to eq(created_object)
  end

  created_object = SomeCreateService.create!
end


This works, though:


it "does something" do
  called_with_object = nil
  expect(SomeProcessService).to receive(:process) do |object|
    called_with_object = object
  end

  created_object = SomeCreateService.create!
  expect(created_object).to eq(called_with_object)
end


Is there a nicer way? (Like delaying all the receive expectations to the end of the example)

Myron Marston

unread,
Jul 15, 2014, 5:02:03 PM7/15/14
to rs...@googlegroups.com
This is simply how closures work: it retains access to local variables that exist when the closure is defined, but not to local variables that are created later on.

Two simple solutions:

* Use an instance variable rather than a local variable for `created_object`.  This does not rely on the semantics of the closure and instead will rely on the value of `self` (which will be the same in both scopes).
* Treat `SomeProcessService` as a spy and use `expect(SomeProessService).to have_received` at the end of your example.  Our docs [1] have more information on spies.

HTH,
Myron

Elad Ossadon

unread,
Jul 15, 2014, 5:40:15 PM7/15/14
to rs...@googlegroups.com
have_received! Exactly what I was looking for. Thanks.

it "does something" do
  allow(SomeProcessService).to receive(:process)
  created_object = SomeCreateService.create!
  expect(SomeProcessService).to have_received(:process).with(created_object)
end

Aaron Kromer

unread,
Jul 15, 2014, 6:19:32 PM7/15/14
to rs...@googlegroups.com

Just to throw this out there, but it seems this spec has helped identify a sticky point in your current design. Perhaps dependency injection would be helpful here.

You could easily inject an instance_double("SomeProcessService").as_null_object. If necessary, you could also create a new fake class that does something more complex. That would allow other more invasive verifications without worrying about the actual process service internals.



--
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/02c0f8d7-85e6-4ef0-b7c2-0d90c09ce413%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages