Stubbing through multiple layers

Hi list,

I’m writing an example for a class that represents the “pickaxe” e-book,
which I view in Preview.app:

describe PickaxeBook do

it “should tell whether it’s ready to read” do
preview = stub(‘preview’)
preview.stub!(:this is what I’m not sure about - see
below
*).returns(“programming_ruby_1.9.pdf”)

my_pickaxe_book = PickaxeBook.new(preview)

my_pickaxe_book.should be_ready_to_read

end
end

class PickaxeBook

def ready_to_read?
#****** this is the dependency I want to stub:
preview.front_window.title ********
preview.front_window.title =~ /programming_ruby_1.9.pdf.*/
end
end

Can you see the problem? ready_to_read wants to query the object
returned by front_window, but how do I stub that? I should note that
the Preview class is in a different library, which is why I don’t just
change front_window.title

I thought of a few things, like introducing a role (something like
“PreviewInfoRetriever”) with a method “front_window_title” that forwards
to preview.front_window.title, but that seems like a lot of complexity.

To summarize: what’s the best way to handle a dependency like
object.method.method?

Thanks!

Sean DeNigris
[email protected]

On Fri, Jan 8, 2010 at 9:37 PM, DeNigris S. [email protected]
wrote:

below*******).returns(“programming_ruby_1.9.pdf”)
#****** this is the dependency I want to stub:
I thought of a few things, like introducing a role (something like
“PreviewInfoRetriever”) with a method “front_window_title” that forwards to
preview.front_window.title, but that seems like a lot of complexity.

To summarize: what’s the best way to handle a dependency like
object.method.method?

There is no universally best way. Stubbing it is fairly straightforward:

window = stub(‘window’, :title => “programming_ruby_1.9.pdf”)
preview = stub(‘preview’, :front_window => window)

The risk is that this example is now bound to the internal structure of
an
object provided by a 3rd party, which can change in a future release. If
that happened, this example might continue passing with a false
positive,
and the app won’t work.

If you have good high level specs with something like Cucumber or
FitNesse,
this risk is diminished. You’ll still have to change the example when
the
internal structure of Preview changes, but you’re less likely to get
burned
by deploying code that passes its tests but doesn’t work.

If it’s easy and inexpensive to create a real instance of Preview, I’d
sooner do that.

HTH,
David

window = stub(‘window’, :title => “programming_ruby_1.9.pdf”)
preview = stub(‘preview’, :front_window => window)
Perfect - so easy! Thanks.

If you have good high level specs with something like Cucumber or FitNesse,
this risk is diminished. You’ll still have to change the example when the
internal structure of Preview changes, but you’re less likely to get burned
by deploying code that passes its tests but doesn’t work.
This is something I’ve been going back and forth with a lot lately -
how much implementation detail to allow to leak out into the examples/
features. If I start using software other than Preview to read my
eBooks, obviously all these examples will require reworking. The
problem is magnified because I do have Cucumber tests that use the
real Preview:

Then /^I should see the pickaxe book$/ do
front_preview_window = Appscript::app(‘System Events’).processes
[‘Preview’].windows[1]
Appscript::app(‘System Events’).processes
[‘Preview’].frontmost.get.should be(true)
front_preview_window.name.get.should =~ /programming_ruby_1.9.*
.pdf.*/
end

Now details about Preview appear in Cucumber and Rspec! I’m tempted
to put an EBookViewer role in right off the bat (similar to what Matt
sugested), and use it in both the features and examples, but I don’t
want to add too much complexity.

I’m very interested in how other people handle external dependencies
in features/examples…

Thanks again!

Sean

On 9 Jan 2010, at 12:02, David C. wrote:

           preview = stub('preview')


that the Preview class is in a different library, which is why I
There is no universally best way. Stubbing it is fairly
If you have good high level specs with something like Cucumber or
FitNesse, this risk is diminished. You’ll still have to change the
example when the internal structure of Preview changes, but you’re
less likely to get burned by deploying code that passes its tests
but doesn’t work.

If it’s easy and inexpensive to create a real instance of Preview,
I’d sooner do that.

Or wrap the Preview object with your own (maybe PreviewWindow?) and
offer a #has_title? method on that wrapper.


rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users


rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users

cheers,
Matt

+447974 430184

On Sat, Jan 9, 2010 at 9:38 PM, Sean DeNigris
[email protected]wrote:

This is something I’ve been going back and forth with a lot lately -
[‘Preview’].frontmost.get.should be(true)
front_preview_window.name.get.should =~ /programming_ruby_1.9.*
.pdf.*/
end

Now details about Preview appear in Cucumber and Rspec! I’m tempted
to put an EBookViewer role in right off the bat (similar to what Matt
sugested), and use it in both the features and examples, but I don’t
want to add too much complexity.

I think it’s clear that there is already too much complexity in this
case :slight_smile:

I’d go with the wrapper. It should not be expensive to maintain, and it
does
give you the freedom to change the dependencies, localizing the cost of
that
change to a single object.

On Sat, Jan 9, 2010 at 7:38 PM, Sean DeNigris
[email protected] wrote:

 window = stub(‘window’, :title => “programming_ruby_1.9.pdf”)
 preview = stub(‘preview’, :front_window => window)
Perfect - so easy! Â Thanks.

There’s also stub_chain, which is probably a Bad Thing but helps break
through layers of dependencies.