Forum: RSpec modules and stub

Posted by Sarah Allen (ultrasaurus)
on 2009-05-25 01:15
If there is a method defined on a module, how do I stub it?

Imagine I have a module like this:

Module Foo
   Module Bar

    def self.do_something(path)
  ...
    end


   end
end


Somewhere in the code, there is this call:
               Foo::Bar::do_something(path)

In my test I want the above call to do nothing, so I tried this:
    before(:each) do
      Foo::Bar.stub!(:do_something).and_return
    end

but I confirm that do_something is getting called by adding a puts in
it. Do I not have the module/stub syntax correct?  How can I isolate
this further?

I tried looking at the doc ( http://rspec.rubyforge.org/rspec/1.2.6/ )
but I couldn't really make sense of it.

Thanks,
Sarah
Posted by David Chelimsky (Guest)
on 2009-05-25 05:59
(Received via mailing list)
On Sun, May 24, 2009 at 7:15 PM, Sarah Allen <lists@ruby-forum.com> 
wrote:
>
>      Foo::Bar.stub!(:do_something).and_return
>    end
>
> but I confirm that do_something is getting called by adding a puts in
> it. Do I not have the module/stub syntax correct?  How can I isolate
> this further?
>
> I tried looking at the doc ( http://rspec.rubyforge.org/rspec/1.2.6/ )
> but I couldn't really make sense of it.

Stub methods on objects, not modules. The method can be one that
*comes from* a module, but you need to stub it on the specific object
that is at play in the example.

module Foo
  def do_something
end

class Bar
  include Foo
end

describe Bar do
  before(:each) do
    @bar = Bar.new
    @bar.stub(:do_something)
  end
  ...
end

HTH,
David
Posted by Sarah Allen (ultrasaurus)
on 2009-05-25 06:03
David Chelimsky wrote:
> Stub methods on objects, not modules. The method can be one that
> *comes from* a module, but you need to stub it on the specific object
> that is at play in the example.

That makes sense, except the code does this:
 Foo::Bar::do_something(path)

I'm not an expert with modules, but that looks like it is calling the 
method directly on the module without an object.  Is there any way for 
me to stub that?  or is that not a good thing to be doing?

Thanks,
Sarah
Posted by David Chelimsky (Guest)
on 2009-05-25 07:23
(Received via mailing list)
On Mon, May 25, 2009 at 12:03 AM, Sarah Allen <lists@ruby-forum.com> 
wrote:
> David Chelimsky wrote:
>> Stub methods on objects, not modules. The method can be one that
>> *comes from* a module, but you need to stub it on the specific object
>> that is at play in the example.
>
> That makes sense, except the code does this:
>  Foo::Bar::do_something(path)
>
> I'm not an expert with modules, but that looks like it is calling the
> method directly on the module without an object.

You're correct.

> Is there any way for me to stub that?

You can stub that like this:

Foo::Bar.stub(:do_something)

> or is that not a good thing to be doing?

This really depends on a lot of different factors. Whether Bar is a
module or a class, do_something is essentially a global, and globals
are *generally* problematic for testing because they increase the risk
of leaking state from example to example. That said, the four mock
frameworks supported directly by rspec (rspec, mocha, flexmock and rr)
all roll back stubs after each example and they all seem to work just
fine. Therefore that risk is mitigated. But that doesn't mean it's
100% risk-free. Ruby is very flexible and powerful, and just as
mock/stub frameworks can momentarily change an object's behaviour, so
can any library code or application code.

If you stub, for example, do_something on this module, but it turns
out that do_something gets added to the module through some dynamic
means *after the stub declaration*, the stub declaration will be
overwritten and you'll get surprising results. This is not just true
of globals - it's true of any methods that appear through the magic of
metaprogramming.

HTH,
David
Posted by Sarah Allen (ultrasaurus)
on 2009-05-25 07:51
David Chelimsky wrote:
>
> Foo::Bar.stub(:do_something)

hmm. that's pretty close to where I started.

> If you stub, for example, do_something on this module, but it turns
> out that do_something gets added to the module through some dynamic
> means *after the stub declaration*, the stub declaration will be
> overwritten and you'll get surprising results. This is not just true
> of globals - it's true of any methods that appear through the magic of
> metaprogramming.

Ah, the joys of dynamic languages.  After further looking at the code, I 
see that the module 'require' is intentionally inside an if, so that it 
will only get loaded as needed.  If I explicitly require file containing 
the module in the spec.rb file, then it works.  I suppose the require in 
the code I'm testing will only have an effect when it's not present in 
the calling code.  Still puzzled why I could call the module before 
without getting a syntax error.

Thanks so much for your help.

Sarah
Posted by David Chelimsky (Guest)
on 2009-05-27 11:00
(Received via mailing list)
On Mon, May 25, 2009 at 1:51 AM, Sarah Allen <lists@ruby-forum.com> 
wrote:
>> of globals - it's true of any methods that appear through the magic of
>> metaprogramming.
>
> Ah, the joys of dynamic languages.  After further looking at the code, I
> see that the module 'require' is intentionally inside an if, so that it
> will only get loaded as needed.  If I explicitly require file containing
> the module in the spec.rb file, then it works.  I suppose the require in
> the code I'm testing will only have an effect when it's not present in
> the calling code.  Still puzzled why I could call the module before
> without getting a syntax error.

By "before" do you mean in an earlier version of your app or of rspec?
Or do you mean within the app somewhere?
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.