-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add --retry option to retry failed tests as part of the same run #920
Changes from 13 commits
745fb95
ba28a95
28bf14e
788a03e
d459c6e
06aa0d7
5ca0ce8
3d843fa
d5979ad
3064b27
b05303f
130a686
3388fe2
7fddbed
7f4b9f2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,14 +12,11 @@ rvm: | |
branches: | ||
only: | ||
- master | ||
- resolve-issue-882 | ||
- v1.3.x-bugfix | ||
|
||
before_install: | ||
- gem update bundler | ||
|
||
notifications: | ||
email: | ||
- [email protected] | ||
webhooks: | ||
urls: # gitter | ||
- https://webhooks.gitter.im/e/dc010332f9d40fcc21c4 | ||
email: false |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
@wip | ||
Feature: Retry failing tests | ||
|
||
Retry gives you a way to get through flaky tests that usually pass after a few runs. | ||
This gives a development team a way forward other than disabling a valuable test. | ||
|
||
- Specify max retry count in option | ||
- Output information to the screen | ||
- Output retry information in test report | ||
|
||
Questions: | ||
use a tag for flaky tests? Global option to retry any test that fails? | ||
|
||
Background: | ||
Given a scenario "Flakey" that fails once, then passes | ||
And a scenario "Shakey" that fails twice, then passes | ||
And a scenario "Solid" that passes | ||
And a scenario "No Dice" that fails | ||
|
||
Scenario: | ||
When I run `cucumber -q --retry 1` | ||
Then it should fail with: | ||
""" | ||
4 scenarios (2 passed, 2 failed) | ||
""" | ||
|
||
Scenario: | ||
When I run `cucumber -q --retry 2` | ||
Then it should pass with: | ||
""" | ||
4 scenarios (3 passed, 1 failed) | ||
""" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
require 'cucumber/core/filter' | ||
require 'cucumber/running_test_case' | ||
require 'cucumber/events/bus' | ||
require 'cucumber/events/after_test_case' | ||
|
||
module Cucumber | ||
module Filters | ||
class Retry < Core::Filter.new(:configuration) | ||
|
||
def test_case(test_case) | ||
configuration.on_event(:after_test_case) do |event| | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd have thought you'd need to register the event listener earlier than this, like maybe in the filter's constructor. Does it work? |
||
next unless retry_required?(test_case, event) | ||
|
||
test_case_counts[test_case] += 1 | ||
event.test_case.describe_to(receiver) | ||
end | ||
|
||
super | ||
end | ||
|
||
private | ||
|
||
def retry_required?(test_case, event) | ||
event.test_case == test_case && event.result.failed? && test_case_counts[test_case] < configuration.retry_attempts | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does not work, since the test_case you pass on is never the same that are in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've resolved this with cucumber/cucumber-ruby-core#111 which makes equality for |
||
end | ||
|
||
def test_case_counts | ||
@test_case_counts ||= Hash.new {|h,k| h[k] = 0 } | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
require 'cucumber' | ||
require 'cucumber/filters/retry' | ||
require 'cucumber/core/gherkin/writer' | ||
require 'cucumber/configuration' | ||
require 'cucumber/core/test/case' | ||
require 'cucumber/core' | ||
require 'cucumber/events' | ||
|
||
describe Cucumber::Filters::Retry do | ||
include Cucumber::Core::Gherkin::Writer | ||
include Cucumber::Core | ||
include Cucumber::Events | ||
|
||
let(:configuration) { Cucumber::Configuration.new(:retry => 2) } | ||
let(:test_case) { Cucumber::Core::Test::Case.new([double('test steps')], double('source').as_null_object) } | ||
let(:receiver) { double('receiver').as_null_object } | ||
let(:filter) { Cucumber::Filters::Retry.new(configuration, receiver) } | ||
let(:fail) { Cucumber::Events::AfterTestCase.new(test_case, double('result', :failed? => true, :ok? => false)) } | ||
let(:pass) { Cucumber::Events::AfterTestCase.new(test_case, double('result', :failed? => false, :ok? => true)) } | ||
|
||
it { is_expected.to respond_to(:test_case) } | ||
it { is_expected.to respond_to(:with_receiver) } | ||
it { is_expected.to respond_to(:done) } | ||
|
||
context "general" do | ||
before(:each) do | ||
filter.with_receiver(receiver) | ||
end | ||
|
||
it "registers the :after_test_case event" do | ||
expect(configuration).to receive(:on_event).with(:after_test_case) | ||
filter.test_case(test_case) | ||
end | ||
end | ||
|
||
context "passing test case" do | ||
it "describes the test case once" do | ||
expect(test_case).to receive(:describe_to).with(receiver) | ||
filter.test_case(test_case) | ||
configuration.notify(pass) | ||
end | ||
end | ||
|
||
context "failing test case" do | ||
it "describes the test case the specified number of times" do | ||
expect(receiver).to receive(:test_case) do |test_case| | ||
configuration.notify(fail) | ||
end.exactly(3).times | ||
|
||
filter.test_case(test_case) | ||
end | ||
end | ||
|
||
context "flaky test cases" do | ||
|
||
context "a little flaky" do | ||
it "describes the test case twice" do | ||
results = [fail, pass] | ||
expect(receiver).to receive(:test_case) do |test_case| | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor style nit-pick (we really should try and get Rubocop going) but I like to see There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also done! |
||
configuration.notify(results.shift) | ||
end.exactly(2).times | ||
|
||
filter.test_case(test_case) | ||
end | ||
end | ||
|
||
context "really flaky" do | ||
it "describes the test case 3 times" do | ||
results = [fail, fail, pass] | ||
|
||
expect(receiver).to receive(:test_case) do |test_case| | ||
configuration.notify(results.shift) | ||
end.exactly(3).times | ||
|
||
filter.test_case(test_case) | ||
end | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think maybe these should go into a
retry_steps.rb
? They're quite specific...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.