[rspec-users] Verifying that a block calls a method

David Chelimsky dchelimsky at gmail.com
Wed Apr 18 07:00:34 EDT 2007


On 4/18/07, aslak hellesoy <aslak.hellesoy at gmail.com> wrote:
> On 4/18/07, Scott Taylor <mailing_lists at railsnewbie.com> wrote:
> >
> > On Apr 17, 2007, at 10:43 PM, David Chelimsky wrote:
> >
> > > On 4/17/07, Scott Taylor <mailing_lists at railsnewbie.com> wrote:
> > >> The only clear advise I've gotten is to
> > >> pass my_fun3 a stream, and let the default parameter be a STDOUT
> > >> stream, like so:
> > >>
> > >> def my_fun3(stream = STDOUT)
> > >>    [1,2,3].each { |x| stream.puts x }
> > >> end
> > >>
> > >>
> > >> Every time you use this method in production, you just leave off the
> > >> parameter, and it defaults to calling puts (as normal).  The stream
> > >> parameter is only used during testing.  This is a little disturbing,
> > >> because now you have to change the source to fit your tests for code
> > >> that will never be used in production.
> > >
> > > This is a very, very common approach in static languages - often used
> > > to break dependencies found in legacy (read: un-tested) code.
> > >
> > > Designing for testability is a tricky thing, for exactly the reason
> > > you describe. You end up designing things differently than you would
> > > if you didn't care about testing. The trick is finding the boundaries
> > > that you're comfortable with. I'm not comfortable with methods that
> > > are only used in tests. Parameterized methods or constructors don't
> > > bug me at all. They serve the tests, and impose low risk of misuse.
> > > That risk is lowered, in my opinion, if you use a constructor
> > > parameterize the constructor rather than the method.
> > >
> > > class MyClass
> > >   def initialize(io=STDOUT)
> > >     @io = io
> > >   end
> > >   def my_fun3
> > >     [1,2,3].each { |x| @io.puts x }
> > >   end
> > > end
> > >
> > > Hope that helps.
> >
> > Well it seems to be good advise.  Unfortunately, it doesn't help me
> > with the Kernel#trap problem, though, which is what the email was
> > originally about.
> >
>
> If you want a concise answer, ask a short and concise question ;-) Try this:
>
> class Mooky
>   def add_sigint_handler(trapper=Kernel)
>     trapper.trap("INT") { raise Interrupt }
>   end
> end
>
> describe Mooky do
>   it 'should install an INT trap that raises an Interrupted' do
>     mooky = Mooky.new
>     trapper = mock('Kernel')
>     trapper.should_receive(:trap).with("INT").and_yield
>     lambda do
>       mooky.add_sigint_handler(trapper)
>     end.should raise_error(Interrupt)
>   end
> end
>
> Any other suggestions?

It would be nice to actually send the signal. This, for example, works
in isolation:

require 'rubygems'
require 'spec'

class Trapper
  def add_sigint_handler
    trap("INT") { raise "trapped" }
  end
end

describe "interrupt" do
  it "should provide trap for INT" do
    lambda do
      Trapper.new.add_sigint_handler
      Process.kill("INT", 0)
    end.should raise_error(RuntimeError, "trapped")
  end
end

The problem is that RSpec handles Interrupt in its own way, and with
this you've now changed the way rspec is going to behave when it
receives Interrupt so you're likely to get some unexpected results.

David

>
> Aslak
>
> > Scott
> >
> >
> >
> > >
> > > David
> > > _______________________________________________
> > > rspec-users mailing list
> > > rspec-users at rubyforge.org
> > > http://rubyforge.org/mailman/listinfo/rspec-users
> >
> > _______________________________________________
> > rspec-users mailing list
> > rspec-users at rubyforge.org
> > http://rubyforge.org/mailman/listinfo/rspec-users
> >
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>


More information about the rspec-users mailing list