RSpec::Wait
Wait for conditions in RSpec
Why does RSpec::Wait exist?
Timing is hard.
Timing problems and race conditions can plague your test suite. As your test suite slowly becomes less reliable, development speed and quality suffer.
RSpec::Wait strives to make it easier to test asynchronous or slow interactions.
How does RSpec::Wait work?
RSpec::Wait allows you to wait for an assertion to pass, using the RSpec syntactic sugar that you already know and love.
RSpec::Wait will keep trying until your assertion passes or times out.
Example
RSpec::Wait's wait_for
assertions are nearly drop-in replacements for RSpec's
expect
assertions. The major difference is that the wait_for
method only
works with non-block matchers. However, wait_for
will still accept a block
because it may need to evaluate the content of that block multiple times while
waiting.
describe Ticker do
subject(:ticker) { Ticker.new("foo") }
describe "#start" do
before do
ticker.start
end
it "starts a blank tape" do
expect(ticker.tape).to eq("")
end
it "writes the message one letter at a time" do
wait_for(ticker.tape).to eq("··-·")
wait_for(ticker.tape).to eq("··-· ---")
wait_for(ticker.tape).to eq("··-· --- ---")
end
end
end
This can be especially useful for testing user interfaces with tricky timing elements like JavaScript interactions or remote requests.
feature "User Login" do
let!(:user) { create(:user, email: "[email protected]", password: "secret") }
scenario "A user can log in successfully" do
visit new_session_path
fill_in "Email", with: "[email protected]"
fill_in "Password", with: "secret"
click_button "Log In"
wait_for { current_path }.to eq(account_path)
expect(page).to have_content("Welcome back!")
end
end
Matchers
RSpec::Wait ties into RSpec's internals so it can take full advantage of any
non-block matcher that you would use with RSpec's own expect
method.
Timeout
By default, RSpec::Wait will wait up to 10 seconds for an assertion to pass. That timeout value is configurable in three ways:
RSpec Configuration
RSpec.configure do |config|
config.wait_timeout = 3 # seconds
end
RSpec Metadata
The timeout can also be specified via options added to a spec's or context's
:wait
metadata:
scenario "A user can log in successfully", wait: { timeout: 3 } do
visit new_session_path
fill_in "Email", with: "[email protected]"
fill_in "Password", with: "secret"
click_button "Log In"
wait_for { current_path }.to eq(account_path)
expect(page).to have_content("Welcome back!")
end
wait
Method
The On a per-assertion basis, the timeout value can be passed to the wait
method.
scenario "A user can log in successfully" do
visit new_session_path
fill_in "Email", with: "[email protected]"
fill_in "Password", with: "secret"
click_button "Log In"
wait(3.seconds).for { current_path }.to eq(account_path)
expect(page).to have_content("Welcome back!")
end
Use with Cucumber
To enable RSpec::Wait in your Cucumber step definitions, add the following to
features/support/env.rb
:
require "rspec/wait"
World(RSpec::Wait)
Who wrote RSpec::Wait?
My name is Steve Richert and I wrote RSpec::Wait in April, 2014 with the support of my employer, Collective Idea. RSpec::Wait owes its current and future success entirely to inspiration and contribution from the Ruby community, especially the authors and maintainers of RSpec.
Thank you!
How can I help?
RSpec::Wait is open source and contributions from the community are encouraged! No contribution is too small.
See RSpec::Wait's contribution guidelines for more information.