A few days ago, I was writing some integration tests. The site's workflow was something like this:
-
POST
to a URL like '/create_a_widget' with some data - Get a token saying "your widget will be A29G"
-
GET
the finished widget from a URL like '/widgets/A29G'.
At step 3, either the widget is ready or you have to try again.
Now I know that it shouldn't take more than, say, 3 seconds before the widget is ready. So in my test, I could say "do step 2, sleep 3
then do step 3 and check the results." But that's slow, and maybe the widget will actually be ready much sooner.
Alternately, I could poll until it's done. That could get results quickly, but would hang if it never finishes.
What I'd really like to do is say "poll for up to 3 seconds. If it finishes sooner, great! If time runs out, give up."
So I wrote a small helper method to do just that. It looks like this:
def with_timeout_of(duration, failure_message = "Time up")
start = Time.now
finish = start + duration
answer = nil
while Time.now < finish
answer = yield
break if answer
sleep 0.1 # simple rate limit
end
answer ? answer : fail(failure_message)
end
Here's how I use it:
it "can make and fetch a widget" do
# 1. do the post
# 2. get the token
widget = with_timeout_of(3) do
response = get_widget('A29G')
# If there's a widget in the response,
# return it; we're done.
# If not, return false and we'll poll
# again if time isn't up.
response.fetch("widget", false)
end
expect(widget).to be_shiny # or whatever
end
If you like this, feel free to adapt it for your own needs. Happy coding!