Forum: RSpec Assertions for asynchronous behaviour

Posted by Matt Wynne (mattwynne)
on 2011-09-13 13:59
(Received via mailing list)
Hi all,

In GOOS[1] they use an assertion called assertEventually which samples 
the system for a success state until a certain timeout has elapsed. This 
allows you to synchronise the tests with asynchronous code.

Do we have an equivalent of that in the Ruby / RSpec world already? I 
know capybara has wait_until { } but that's fairly rudimentary - the 
failure message isn't very helpful. Is there anything else already out 
there?

[1] http://www.growing-object-oriented-software.com/

cheers,
Matt

--
Freelance programmer & coach
Author, http://pragprog.com/book/hwcuc/the-cucumber-book (with Aslak 
Hellesy)
Founder, http://relishapp.com
+44(0)7974430184 | http://twitter.com/mattwynne
Posted by Justin Ko (Guest)
on 2011-09-13 17:33
(Received via mailing list)
On Tue, Sep 13, 2011 at 6:56 AM, Matt Wynne <matt@mattwynne.net> wrote:

> [1] http://www.growing-object-oriented-software.com/
>
>
> _______________________________________________
> rspec-users mailing list
> rspec-users@rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>

the_object.should eventually_call(:foo).within(2).seconds

Sounds like Matt is going to make us this matcher! :)
Posted by Sidu Ponnappa (kaiwren)
on 2011-09-13 19:11
(Received via mailing list)
> the_object.should eventually_call(:foo).within(2).seconds
TDDing multithreaded apps. Good times.

Best,
Sidu.
http://blog.sidu.in
Posted by Alex Chaffee (alexch)
on 2011-09-13 20:01
(Received via mailing list)
Sure. "wait_for" is a method Brian Takita and I originally wrote for
use in Selenium tests, then IIRC it made it into the Selenium gem and
now lots of libraries use it (or their own version -- I make no patent
claim on polling :-)). The wait_for I remember allowed you to
customize the failure message. Let me go see if it's on GitHub or
anything...

Ah, here's one:
https://github.com/pivotal/selenium/blob/master/li...

(Maybe I should put it in Wrong.)

 - A

On Tue, Sep 13, 2011 at 3:56 AM, Matt Wynne <matt@mattwynne.net> wrote:
> --
>
--
Alex Chaffee - alex@stinky.com
http://alexch.github.com
http://twitter.com/alexch
Posted by Matt Wynne (mattwynne)
on 2011-09-20 14:01
(Received via mailing list)
Thanks for all the ideas. I just rolled my own which expects a block 
with an assertion in it:

https://gist.github.com/1228927

Could we put this into RSpec somewhere? I'd rather not dump the source 
into The Cucumber Book - it's too low level. I could put it into it's 
own little gem but that seems like creating clutter in the gemsphere.

WDYT?

On 13 Sep 2011, at 17:41, Alex Chaffee wrote:

> (Maybe I should put it in Wrong.)
>> message isn't very helpful. Is there anything else already out there?
>> _______________________________________________
> http://twitter.com/alexch
> _______________________________________________
> rspec-users mailing list
> rspec-users@rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users

cheers,
Matt

--
Freelance programmer & coach
Author, http://pragprog.com/book/hwcuc/the-cucumber-book (with Aslak 
Hellesy)
Founder, http://relishapp.com
+44(0)7974430184 | http://twitter.com/mattwynne
Posted by Alex Chaffee (alexch)
on 2011-09-21 00:50
(Received via mailing list)
On Tue, Sep 20, 2011 at 4:55 AM, Matt Wynne <matt@mattwynne.net> wrote:
> Thanks for all the ideas. I just rolled my own which expects a block with an
> assertion in it:

I love the language!

    eventually { white.should be_black }

> Could we put this into RSpec somewhere?

It's not actually RSpec-specific. I'll put it (or probably a hybrid
between your new code and my old code) into Wrong and you can use it
via the wrong rspec adapter.

def two
  rand(3)
end

require "rspec"
require "wrong/adapters/rspec"
describe "two" do
  it "should eventually be half of four" do
    eventually { (two + two).should == 4 }
  end
end

should work soonish...

--
Alex Chaffee - alex@stinky.com
http://alexchaffee.com
http://twitter.com/alexch
Posted by Matt Wynne (mattwynne)
on 2011-09-21 13:09
(Received via mailing list)
On 20 Sep 2011, at 23:38, Alex Chaffee wrote:

> It's not actually RSpec-specific. I'll put it (or probably a hybrid
> between your new code and my old code) into Wrong and you can use it
> via the wrong rspec adapter.

Thanks!

I know it's not RSpec specific. I suppose my motivation is I want to 
keep the number of tools / gems we have to mention in the book to a 
minimum. Since we're already using RSpec in the book already it made 
sense to me if it became part of the RSpec assertion toolkit. I'd have 
thought it would be useful to other RSpec users too.

I've never used Wrong, only read about it--and I like the idea very 
much, I must say. Is there any danger of adverse effects (other than an 
extra line the Gemfile) if we have to use the Wrong RSpec adapter in the 
book alongside the existing vanilla RSpec assertions?

>  end
> end
>
> should work soonish...
>
> --
> Alex Chaffee - alex@stinky.com
> http://alexchaffee.com
> http://twitter.com/alexch

cheers,
Matt

--
Freelance programmer & coach
Author, http://pragprog.com/book/hwcuc/the-cucumber-book (with Aslak 
Hellesy)
Founder, http://relishapp.com
+44(0)7974430184 | http://twitter.com/mattwynne
Posted by Alex Chaffee (alexch)
on 2011-09-21 19:11
(Received via mailing list)
On Wed, Sep 21, 2011 at 3:42 AM, Matt Wynne <matt@mattwynne.net> wrote:
> I've never used Wrong, only read about it--and I like the idea very much, I must 
say. Is there any danger of adverse effects (other than an extra line the Gemfile) 
if we have to use the Wrong RSpec adapter in the book alongside the existing 
vanilla RSpec assertions?
>

There shouldn't be, but I'd love for more people to verify that. The
adapter source is at

https://github.com/sconover/wrong/blob/master/lib/...

(It doesn't actually do much, just includes Wrong inside RSpec's
ExampleGroup. The bulk of that code is in case anyone uses the Wrong
feature "alias_assert" which allows users to define their own DSLy
name for assert. I like "expect" but RSpec already has its own
"expect"; David kindly provided a way to cleanly remove RSpec's
"expect" before defining my own, but I couldn't quite get it to work
so I'm still using the brute force way which just chops it out using
Ruby's "Module#remove_method" method. In any case, none of that is
relevant unless users go out of their way and call
"Wrong.config.alias_assert :expect, :override=>true" which most won't
do.)

There's a semantic issue in your eventually method that I'd like to
discuss. My wait_for[1] and friends take a *predicate* (in the form of
a block) and wait for it to return true(ish). Your eventually[2]
ignores the return condition, and merely waits for it to not raise an
error. I think using a predicate is more useful, and strictly no less
powerful since the waiter code will *also* wait for it to not raise an
error, so you could do *either* this

    eventually { rand(10) == 0 }

or this

    eventually { rand(10).should == 0 }

and if, after 2 sec or whatever, it keeps either being false or
raising an ExpectationNotMetError, then the waiter will itself raise
an ExpectationNotMetError.

IOW, should the following pass or should it fail?

    eventually { false == true }

 - A

P.S. (Lurkers please feel free to chime in too. :-)

[1] 
https://github.com/pivotal/selenium/blob/master/li...
[2] https://gist.github.com/1228927

--
Alex Chaffee - alex@stinky.com
http://alexchaffee.com
http://twitter.com/alexch
Posted by Matt Wynne (mattwynne)
on 2011-09-21 23:08
(Received via mailing list)
On 21 Sep 2011, at 17:46, Alex Chaffee wrote:

> or this
>
>    eventually { rand(10).should == 0 }
>
> and if, after 2 sec or whatever, it keeps either being false or
> raising an ExpectationNotMetError, then the waiter will itself raise
> an ExpectationNotMetError.
>
> IOW, should the following pass or should it fail?
>
>    eventually { false == true }

To be honest, I just ignored the true / false version in my 
implementation because I want a helpful error message when the test 
fails, rather than a crude TimeoutError or whatever.

I'm ambivalent about this: I would worry that allowing falsiness to 
cause an assertion to be raised is not idiomatic RSpec, but perhaps it 
is idiomatic Wrong, since you can do all that magic to infer an error 
message anyway, right?

I think as long as I can use my own assertions too I don't have any 
objections to it doing both.

> P.S. (Lurkers please feel free to chime in too. :-)
>
> [1] https://github.com/pivotal/selenium/blob/master/li...
> [2] https://gist.github.com/1228927


cheers,
Matt

--
Freelance programmer & coach
Author, http://pragprog.com/book/hwcuc/the-cucumber-book (with Aslak 
Hellesy)
Founder, http://relishapp.com
+44(0)7974430184 | http://twitter.com/mattwynne
Posted by Alex Chaffee (alexch)
on 2011-09-22 03:06
(Received via mailing list)
On Wed, Sep 21, 2011 at 1:01 PM, Matt Wynne <matt@mattwynne.net> wrote:
> I want a helpful error message when the test fails, rather than a crude 
TimeoutError or whatever.

I hear you.

> I'm ambivalent about this: I would worry that allowing falsiness to cause an 
assertion to be raised is not idiomatic RSpec, but perhaps it is idiomatic Wrong, 
since you can do all that magic to infer an error message anyway, right?

I hope so... I mean, uh, yes! Definitely! :-)

--
Alex Chaffee - alex@stinky.com
http://alexchaffee.com
http://twitter.com/alexch
Posted by Alex Chaffee (alexch)
on 2011-09-28 02:34
(Received via mailing list)
After a week of stealing minutes, I eventually wrote eventually!
Please check this out and give me feedback. I can ship it in a new
Wrong gem as soon as you all tell me it's ready.

docs:
https://github.com/alexch/wrong/commit/cae852f09a3...

test (spec):
https://github.com/alexch/wrong/blob/master/test/e...

code:
https://github.com/alexch/wrong/blob/master/lib/wr...

The only major feature I haven't done is editing the error message
from inside the block, since Wrong seems to do a good job of this on
its own. If the block contains a "should" or a Wrong "assert" then it
ends up looking like it just got called and failed the final time.
It'd be nice if I could sneak into an exception and append "(after 5
sec)" to e.message but I don't know if I want to go there...

--
Alex Chaffee - alex@stinky.com
http://alexchaffee.com
http://twitter.com/alexch
Posted by Matt Wynne (mattwynne)
on 2011-10-03 00:03
(Received via mailing list)
On 28 Sep 2011, at 01:09, Alex Chaffee wrote:

> code:
> https://github.com/alexch/wrong/blob/master/lib/wr...
>
> The only major feature I haven't done is editing the error message
> from inside the block, since Wrong seems to do a good job of this on
> its own. If the block contains a "should" or a Wrong "assert" then it
> ends up looking like it just got called and failed the final time.
> It'd be nice if I could sneak into an exception and append "(after 5
> sec)" to e.message but I don't know if I want to go there...

I haven't used it, but this looks good to me.

>
> --
> Alex Chaffee - alex@stinky.com
> http://alexchaffee.com
> http://twitter.com/alexch
> _______________________________________________
> rspec-users mailing list
> rspec-users@rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users

cheers,
Matt

--
Freelance programmer & coach
Author, http://pragprog.com/book/hwcuc/the-cucumber-book (with Aslak 
Hellesy)
Founder, http://relishapp.com
+44(0)7974430184 | http://twitter.com/mattwynne
Posted by Alex Chaffee (alexch)
on 2011-10-13 22:15
(Received via mailing list)
FYI, I've just released Wrong 0.6.0 with eventually "as is" -- i.e. no
extra exception message fiddling.

I also added a message param to Wrong's "d" method, e.g.

    d("math is hard") { 2 + 2 }

prints

    math is hard: (2 + 2) is 4

to the console. Useful for debugging (which is what "d" stands for)
when you don't want the test flow to stop with an assert or should.

 - A

--
Alex Chaffee - alex@stinky.com
http://alexchaffee.com
http://twitter.com/alexch
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.