waitfor

DESCRIPTION:

Simple solution to the sleep( ) test anti-pattern.

Blocks execution until a supplied block returns true, or a specified time interval is reached, at which point an error is raised.

object.some_nonblocking_operation

WaitFor.upto 30.seconds do
  object.completed?
end

FEATURES:

  • Accepts time intervals in two formats

    • Hash with symbols :seconds or :minutes

      • :seconds => 30

      • :minutes => 5

    • Singular forms ( 1.second or :minute => 1 ) accepted as well

    • Legacy mode with seconds or minutes

      • 30.seconds

      • 5.minutes

  • Allows for custom exceptions, custom error messages or both

    • For custom exceptions, add ‘:exception => YourException’ to the options

      • Use either :exception or :error for custom exceptions ( both are identical in functionality )

    • For custom messages, add ‘:message => “Your Custom Message”’ to the options

  • Resolution of delay between Ruby block executions is currently 1 second ( and not yet configurable )

  • Must use hash if specifying custom exceptions or messages, can’t mix and match simple time DSL with hash

SYNOPSIS:

Simple solution to the sleep( ) test anti-pattern.

Blocks execution until a supplied block returns true, or a specified time interval is reached, at which point an error is raised.

Useful for adding elasticity to tests involving asynchronous or non-blocking operations. Instead of sleep( )‘ing in a test, add a WaitFor. Reduces test execution time by waiting only as long as required. Adds robustness by giving test steps enough time to execute, reducing fail-positive failures from timeouts.

Whereas you might normally do something like this:

object.some_asynchronous_operation

# Wait for it to finish and let's hope it doesn't take 31 seconds, because then the test might fail without good reason!
# Let's also hope it takes no less than 29 seconds, because then we're making the test slow!
sleep( 30 )

It would be far superior to use WaitFor in this manner:

object.some_asynchronous_operation

WaitFor.upto 60.seconds do
  object.completed?
end

Here the test could take as long as 60 seconds and still pass, likewise, if it took only 1 second, it would continue on with minimal delay. We’ve removed the brittleness that sleep() adds.

Custom exception and message are specified in the examples below.

WaitFor.upto( :minute => 1, :exception => EventFailedToOccurError ) do
  event.happened?
end

WaitFor.upto( :minute => 1, :message => "Event took longer than 1 minute to complete" ) do
  event.happened?
end

WaitFor.upto( :minute => 1, :exception => EventFailedToOccurError, :message => "Event took longer than 1 minute to complete" ) do
  event.happened?
end

Finally, a few more “real world” examples.

page.click "Link"

WaitFor.upto( :minute => 1, :message => "New page did not appear within 1 minute after clicking link" ) do
  page.has_loaded? && page.title == "Home"
end

print "/tmp/file"

WaitFor.upto( 30.seconds ) { files_in_queue( "/tmp/print/queue" ).count == 1 }

REQUIREMENTS:

  • Ruby >= 1.8.6

INSTALL:

sudo gem install waitfor --source http://gemcutter.org

LICENSE:

(The MIT License)

Copyright © 2010-2011 James Bobowski

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.