Quarantine automatically detects flaky tests (i.e. those which fail non-deterministically) and disables them until they're proven reliable.
Quarantine current supports the following testing frameworks. If you need an additional one, please file an issue or open a pull request.
Quarantine should provide the necessary hooks for compatibility with any CI solution. If it's insufficient for yours, please file an issue or open a pull request.
Quarantine works in tandem with RSpec::Retry. Add this to your Gemfile and run bundle install:
group :test do
gem 'quarantine'
gem 'rspec-retry'
endIn your spec_helper.rb, set up Quarantine and RSpec::Retry. See RSpec::Retry's documentation for details of its configuration.
require 'quarantine'
require 'rspec/retry'
Quarantine::RSpecAdapter.bind
RSpec.configure do |config|
# Also accepts `:credentials` to override the standard AWS credential chain
config.quarantine_database = {type: :dynamodb, region: 'us-west-1'}
# Prevent the list of flaky tests from being polluted by local development and PRs
config.quarantine_record_tests = ENV["CI"] && ENV["BRANCH"] == "master"
config.around(:each) do |example|
example.run_with_retry(retry: 3)
end
endQuarantine comes with a CLI tool for setting up the necessary table in DynamoDB, if you use that database.
bundle exec quarantine_dynamodb -h # See all options
bundle exec quarantine_dynamodb \ # Create the "test_statuses" table in us-west-1 in AWS DynamoDB
--region us-west-1A flaky test fails on the first run, but passes after being retried via RSpec::Retry.
require "spec_helper"
describe Quarantine do
it "fails on the first run" do
raise "error" if RSpec.current_example.attempts == 0
# otherwise, pass
end
end$ CI=1 BRANCH=master bundle exec rspec <filename>
[quarantine] Quarantined tests:
./bar_spec.rb[1:1] Quarantine fails on the first runWhen the build completes, all test statuses are written to the database. Flaky tests are marked quarantined, and will be executed in future builds, but any failures will be ignored.
A test can be removed from quarantine by updating the database manually (outside this gem), or by configuring quarantine_release_at_consecutive_passes to remove it after it passes on a certain number of builds in a row.
In spec_helper.rb, you can set configuration variables by doing:
RSpec.configure do |config|
config.VAR_NAME = VALUE
endquarantine_database: Database configuration (see below), default:{ type: :dynamodb, region: 'us-west-1' }test_statusus_table: Table name for test statuses, default:"test_statuses"skip_quarantined_tests: Skipping quarantined tests during test runs default:truequarantine_record_tests: Recording test statuses, default:truequarantine_logging: Outputting quarantined gem info, default:truequarantine_extra_attributes: Storing custom per-example attributes in the table, default:nilquarantine_failsafe_limit: A failsafe limit of quarantined tests in a single run, default:10quarantine_release_at_consecutive_passes: Releasing a test from quarantine after it records enough consecutive passes (nilto disable this feature), default:nil
Quarantine comes with built-in support for the following database types:
:dynamodb:google_sheets
To use :dynamodb, be sure to add gem 'aws-sdk-dynamodb', '~> 1', group: :test to your Gemfile.
To use :google_sheets, be sure to add gem 'google_drive', '~> 3', group: :test to your Gemfile. Here's an example:
config.quarantine_database = {
type: :google_sheets,
authorization: {type: :service_account_key, file: "service_account.json"}, # also accepts `type: :config`
spreadsheet: {
type: :by_key, # also accepts `type: :by_title` and `type: :by_url`
key: "1Jb5fC6wSuIMnP85tUR5knuZ4f5fuu4nMzQF6-0l-EXAMPLE"}, # also accepts `type: :by_title` and `type: :by_url`
}The spreadsheet first line (1) should contains: id, full_description, updated_at, last_status, location, extra_attributes, consecutive_passes. Something like:
| A | B | C | D | E | F | G |
|---|---|---|---|---|---|---|
| id | full_description | updated_at | last_status | location | extra_attributes | consecutive_passes |
To use a custom database that's not provided, subclass Quarantine::Databases::Base and pass an instance of your class as the quarantine_database setting:
class MyDatabase < Quarantine::Databases::Base
...
end
RSpec.configure do |config|
config.quarantine_database = MyDatabase.new(...)
endUse quarantine_extra_attributes to store custom data with each test in the database, e.g. variables useful for your CI setup.
config.quarantine_extra_attributes = Proc.new do |example|
{
build_url: ENV['BUILDKITE_BUILD_URL'],
job_id: ENV['BUILDKITE_JOB_ID'],
}
endQuarantine may be configured to only run in certain environments. Check your spec_helper.rb, and make sure you have all necessary environment variables set, e.g.:
CI=1 BRANCH=master bundle exec rspecThe AWS client attempts to loads credentials from the following locations, in order:
- The optional
credentialsfield inRSpec.configuration.quarantine_database ENV['AWS_ACCESS_KEY_ID']andENV['AWS_SECRET_ACCESS_KEY']Aws.config[:credentials]- The shared credentials ini file at
~/.aws/credentials
More detailed information can be found in the AWS SDK documentation
Example#clear_exception is an attribute added through RSpec::Retry. Make sure it has been installed and configured.