Forum: RSpec Selectively ignoring exceptions in examples

E540cdc2ce3a8fc669d040143ed3d461?d=identicon&s=25 Ash Moran (Guest)
on 2011-08-29 19:10
(Received via mailing list)
Hi all

Long time since I've posted to rspec-users. Glad to see the place is
still here and hope you're all well :-)

I have a question about ignoring exceptions when they're not
interesting. For example, I have a few cases in my code along these
lines

   it "prints an error" do
     expect {
       run_command(%w[ missing_wallet.dat ])
     }.to raise_error

     stream_bundle.captured_error.should eq "Couldn't find wallet file:
missing_wallet.dat\n"
   end

   it "raises a CLI::CommandError" do
     expect {
       run_command(%w[ missing_wallet.dat ])
     }.to raise_error(CLI::CommandError)
   end

But in the first example, I'm only bothered about the output, not the
error. So I was thinking of writing something along the lines of:

   it "prints an error" do
     ignoring_errors {
       run_command(%w[ missing_wallet.dat ])
     }
     stream_bundle.captured_error.should eq "Couldn't find wallet file:
missing_wallet.dat\n"
   end

Now obviously that wouldn't be hard to add as a helper method. But it
got me thinking

* Do any of you do this?
* Does RSpec already let you somehow?
* Is it a useful convention?
* Is it hiding anything else? (I don't use exceptions much, so I may be
abusing them.)

Cheers
Ash

--
http://www.patchspace.co.uk/
http://www.linkedin.com/in/ashmoran
49de4cd2f26705785cbef2b15a9df7aa?d=identicon&s=25 Nick Hoffman (nickh)
on 2011-08-29 22:55
(Received via mailing list)
Hey there, Ash. Why not put your call to #run_command inside a
begin-rescue
statement?
E540cdc2ce3a8fc669d040143ed3d461?d=identicon&s=25 Ash Moran (Guest)
on 2011-08-29 23:11
(Received via mailing list)
On 29 Aug 2011, at 20:09, Nick wrote:

> Hey there, Ash. Why not put your call to #run_command inside a begin-rescue
statement?

Hi Nick

Yes I'm missing the obvious as usual* :-)

Well I guess I could, but the syntax then is even more intrusive. I
guess I just want the lightest way possible to say "Yes, I know this
code fails, but I still want to know what's going on inside it!" That's
why I wondered maybe I should give more thought to how I'm using
exceptions. (I'm raising a CLI::CommandError anywhere in a Command class
where I know I don't want the CLI to proceed any further.)

Cheers
Ash

* I'm sorry, I wrote the code on my own, my pair was actually taking his
bank holiday off :)

--
http://www.patchspace.co.uk/
http://www.linkedin.com/in/ashmoran
2cddfa1d5e719e79aeab4e351c1793e9?d=identicon&s=25 Alex Chaffee (alexch)
on 2011-08-30 02:04
(Received via mailing list)
On Mon, Aug 29, 2011 at 9:24 AM, Ash Moran <ash.moran@patchspace.co.uk>
wrote:
>  it "prints an error" do
>   ignoring_errors {
>    run_command(%w[ missing_wallet.dat ])
>   }
>   stream_bundle.captured_error.should eq "Couldn't find wallet file:
missing_wallet.dat\n"
>  end
>
> Now obviously that wouldn't be hard to add as a helper method. But it got me
thinking
>
> * Do any of you do this?

I do. So often that I wrote a helper and put it in Wrong.

I don't quite get what "stream_bundle.captured_error" is in your
example, but I think the above example would become

rescuing {
  run_command(%w[ missing_wallet.dat ])
}.message.should == "Couldn't find whatever"

We've also got "capturing" for grabbing console output, e.g.

capturing { puts "hi" }.should == "hi"

or

out, err = capturing(:stdout, :stderr) { ... }

See https://github.com/sconover/wrong

--
Alex Chaffee - alex@stinky.com
http://alexch.github.com
http://twitter.com/alexch
E540cdc2ce3a8fc669d040143ed3d461?d=identicon&s=25 Ash Moran (Guest)
on 2011-08-30 12:45
(Received via mailing list)
On 30 Aug 2011, at 00:09, Alex Chaffee wrote:

> I do. So often that I wrote a helper and put it in Wrong.

Cool, and also I really should try Wrong! I've just put it on my project
TODO list as something to investigate

> I don't quite get what "stream_bundle.captured_error" is in your
> example, but I think the above example would become
>
> rescuing {
>  run_command(%w[ missing_wallet.dat ])
> }.message.should == "Couldn't find whatever"
>
> We've also got "capturing" for grabbing console output, e.g.
>
> capturing { puts "hi" }.should == "hi"

Sorry, I didn't realise how opaque that is if you don't know what one
is.

Basically, I wanted to decouple the app from STDIN/STDOUT/STDERR so I
started injecting StringIO objects around for testing. After a while I
had a data clump of @input/@output/@error in classes everywhere, so I
made a StreamBundle[1] to encapsulate them. (Apologies for lack of
syntax highlighting on patch-tag.com)

But then I realised my tests were making a
`StreamBundle.new(StringIO.new, StringIO.new, StringIO.new)` everywhere,
so I factored out the duplication into a CapturingStreamBundle[2]
decorator that records all output to secondary StringIO objects. Now I
just call `CapturingStreamBundle.test_bundle` to get one.

I've found this pattern useful as now that only files in my bin/ folder
ever access ARGV, STDOUT etc, the code is more loosely coupled, but also
I can see exactly where I'm talking to the outside world. Every time I
want to send some output, I think- "Should I really be giving this
object a StreamBundle? Is it really appropriate for it to be talking to
the user directly?"

That's the backstory anyway!

Ash

[1]
https://patch-tag.com/r/ashmoran/rbcoin/snapshot/c...
[2]
https://patch-tag.com/r/ashmoran/rbcoin/snapshot/c...

--
http://www.patchspace.co.uk/
http://www.linkedin.com/in/ashmoran
2cddfa1d5e719e79aeab4e351c1793e9?d=identicon&s=25 Alex Chaffee (alexch)
on 2011-08-30 20:45
(Received via mailing list)
> I've found this pattern useful as now that only files in my bin/ folder ever
access ARGV, STDOUT etc, the code is more loosely coupled, but also I can see
exactly where I'm talking to the outside world. Every time I want to send some
output, I think- "Should I really be giving this object a StreamBundle? Is it
really appropriate for it to be talking to the user directly?"

I hear you. When I first started coding in Ruby I was injecting all
over the place. But nowadays I'm much more comfortable with the
flexibility of things like reopening classes during tests, or mocking
methods, or resetting globals...

...or grabbing and reassigning $stdout and $stderr, which is what
"capturing" does.

The basic idea is that Ruby is *already* decoupled from stdin/out/err
via its dynamic nature and $globals. I get that by naming the inputs
explicitly you're ensuring 100% compliance but you should consider
whether it's worth it. Passing around DI clumps (aka "value objects"
or "parameter objects" in some circles) can make your code a lot less
concise, and concision is one of the great joys of Ruby.

btw apologies if you already know this, but inside a normal Ruby
program you should always use $stderr/$stdout/$stdin, not
STDERR/STDOUT/STDIN since the former are settable and the latter are
hardcoded to the "real" streams and, as true CONSTANTS, not easy to
change. For whatever reason I see the caps versions used a lot more
than the dollar versions, which is a shame.

--
Alex Chaffee - alex@stinky.com
http://alexch.github.com
http://twitter.com/alexch
E540cdc2ce3a8fc669d040143ed3d461?d=identicon&s=25 Ash Moran (Guest)
on 2011-08-31 00:30
(Received via mailing list)
On 30 Aug 2011, at 19:24, Alex Chaffee wrote:

> ...or grabbing and reassigning $stdout and $stderr, which is what
> "capturing" does.
>
> The basic idea is that Ruby is *already* decoupled from stdin/out/err
> via its dynamic nature and $globals. I get that by naming the inputs
> explicitly you're ensuring 100% compliance but you should consider
> whether it's worth it. Passing around DI clumps (aka "value objects"
> or "parameter objects" in some circles) can make your code a lot less
> concise, and concision is one of the great joys of Ruby.

Yes, I've done constant reassigning in the past, with before/after
blocks to control the environment. I wasn't specifically avoiding that
here, it's just what fell out my my initial design to pass the standard
streams in as variables from the bin/ command. I'm going to see how it
pans out, as I've never been so strict about IO before.


> btw apologies if you already know this, but inside a normal Ruby
> program you should always use $stderr/$stdout/$stdin, not
> STDERR/STDOUT/STDIN since the former are settable and the latter are
> hardcoded to the "real" streams and, as true CONSTANTS, not easy to
> change. For whatever reason I see the caps versions used a lot more
> than the dollar versions, which is a shame.

Actually I didn't know that, so thanks for pointing it out :-)  Although
I have often been puzzled by the presence of both forms


Cheers
Ash

--
http://www.patchspace.co.uk/
http://www.linkedin.com/in/ashmoran
This topic is locked and can not be replied to.