rspec-rebound 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e912934fa33c3b36080535612810431e72564925d5fc9448e0a96254dc0e13a
4
- data.tar.gz: 9a0b3c4a4f32979120e4da4b7a6c4cfe61cf2a57f3b0ee16da0112f2e4e06bcd
3
+ metadata.gz: 6c9b0d8334e56e55874fe4d50fa2cf54477d3444eabffb627b674bae6a9f0640
4
+ data.tar.gz: 6526adcc46e3414c70d6503838b5a36bd8d06448ced51f12c541c1c0d78770f5
5
5
  SHA512:
6
- metadata.gz: f5983ecd3470416e8f5407bab05f11b900637b66953d881734095537143a7aea4ba6f2deae807cc10dddd6169989855f3c772ec2c8ece696be58b001586273ec
7
- data.tar.gz: 27527c62988cee2af25cfdac853c3456f5e288e5060d59ce131b3476e92160102cf962d5e89c1a6e0acc12013cdabf2e3e6d8a276db23b6e0cea4576b7d43b6c
6
+ metadata.gz: e468c341112a4eb362d3024dce99a531cf170c210b04f15f73fe96cafde5af9fb1359916ab8d452f11ed64562597aee397ba2bed317298b14917692c38c03b6c
7
+ data.tar.gz: e3fee92d0f401f16684b48d3a5a3e3333d06923ceb7e34d7c4fd287b30e8d35d8357aee87a525ed78bd7ec664068a79f018057a41a6ce4f1e81f547728402b18
data/README.md CHANGED
@@ -51,6 +51,11 @@ RSpec.configure do |config|
51
51
  ex.run_with_retry retry: 3
52
52
  end
53
53
 
54
+ # callback to be run when a flaky test is detected
55
+ config.flaky_test_callback = proc do |example|
56
+ Rspec::Watchdog::Reporter.report(example)
57
+ end
58
+
54
59
  # callback to be run between retries
55
60
  config.retry_callback = proc do |ex|
56
61
  # run some additional clean up task - can be filtered by example metadata
@@ -90,10 +95,11 @@ You can call `ex.run_with_retry(opts)` on an individual example.
90
95
  - __:exceptions_to_hard_fail__(default: *[]*) List of exceptions that will trigger an immediate test failure without retry. Takes precedence over __:exceptions_to_retry__
91
96
  - __:exceptions_to_retry__(default: *[]*) List of exceptions that will trigger a retry (when empty, all exceptions will)
92
97
  - __:retry_callback__(default: *nil*) Callback function to be called between retries
93
-
98
+ - __:flaky_test_callback__(default: *nil*) Callback function to be called when a flaky test is detected (when a test fails but then passes on a subsequent attempt)
99
+ - __:flaky_spec_detection__(default: *false*) If true, flaky tests will be detected and reported, even if the retry count is set to 0. This is useful for detecting flaky tests that are not being retried.
94
100
 
95
101
  ## Environment Variables
96
- - __RSPEC_RETRY_RETRY_COUNT__ can override the retry counts even if a retry count is set in an example or default_retry_count is set in a configuration.
102
+ - __RSPEC_REBOUND_RETRY_COUNT__ can override the retry counts even if a retry count is set in an example or default_retry_count is set in a configuration.
97
103
 
98
104
  ## Contributing
99
105
 
data/changelog.md CHANGED
@@ -92,16 +92,20 @@ rspec-retry now supports rspec 3.3. (thanks @eitoball, #32)
92
92
  include travis configuration for testing rspec 3.2.* and 3.3.*
93
93
  (thanks @eitoball, #31)
94
94
 
95
- # Changelog
96
-
97
- ## 0.1.0 (YYYY-MM-DD)
95
+ ## 0.1.0 (2025-03-22)
98
96
 
99
97
  ### Added
100
98
  - Initial release
101
- - [List major features]
99
+ - All respec-retry features
102
100
 
103
- ### Changed
104
- - [List changes]
101
+ ## 0.2.0 (2025-03-29)
105
102
 
106
- ### Fixed
107
- - [List fixes]
103
+ ### Added
104
+ - Added support for RSpec 3.10
105
+ - Added support for Ruby 3.1
106
+ - Added support for Ruby 3.2
107
+ - Added flaky_test_callback
108
+ - Added flaky_spec_detection
109
+
110
+ ## 0.2.1 (2025-04-01)
111
+ - Fixed a bug where the default retry count was 1 instead of 0
@@ -41,14 +41,12 @@ class RSpec::Rebound::Formatter < RSpec::Core::Formatters::BaseTextFormatter
41
41
  private
42
42
 
43
43
  def increment_success(example)
44
- # debugger
45
44
  previous = @tries[example]
46
45
  @tries[example] = {
47
46
  successes: previous[:successes] + 1, tries: previous[:tries] + 1 }
48
47
  end
49
48
 
50
49
  def increment_tries(example)
51
- # debugger
52
50
  previous = @tries[example]
53
51
  @tries[example] = {
54
52
  successes: previous[:successes], tries: previous[:tries] + 1 }
@@ -1,5 +1,5 @@
1
1
  module RSpec
2
2
  class Rebound
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.1"
4
4
  end
5
5
  end
data/lib/rspec/rebound.rb CHANGED
@@ -7,7 +7,7 @@ module RSpec
7
7
  def self.setup
8
8
  RSpec.configure do |config|
9
9
  config.add_setting :verbose_retry, :default => false
10
- config.add_setting :default_retry_count, :default => 1
10
+ config.add_setting :default_retry_count, :default => 0
11
11
  config.add_setting :default_sleep_interval, :default => 0
12
12
  config.add_setting :exponential_backoff, :default => false
13
13
  config.add_setting :clear_lets_on_failure, :default => true
@@ -33,6 +33,12 @@ module RSpec
33
33
  # Callback between retries
34
34
  config.add_setting :retry_callback, :default => nil
35
35
 
36
+ # Callback for flaky tests
37
+ config.add_setting :flaky_test_callback, :default => nil
38
+
39
+ # If true, flaky tests will be detected and reported, even if the retry count is set to 0. This is useful for detecting flaky tests that are not being retried.
40
+ config.add_setting :flaky_spec_detection, :default => false
41
+
36
42
  config.around(:each) do |ex|
37
43
  ex.run_with_retry
38
44
  end
@@ -54,7 +60,7 @@ module RSpec
54
60
  def retry_count
55
61
  [
56
62
  (
57
- ENV['RSPEC_RETRY_RETRY_COUNT'] ||
63
+ ENV['RSPEC_REBOUND_RETRY_COUNT'] ||
58
64
  ex.metadata[:retry] ||
59
65
  RSpec.configuration.retry_count_condition.call(ex) ||
60
66
  RSpec.configuration.default_retry_count
@@ -122,13 +128,29 @@ module RSpec
122
128
  example.clear_exception
123
129
  ex.run
124
130
 
131
+ if example.exception.nil?
132
+ # If it's a flaky test, call the callback
133
+ if attempts > 0
134
+ if RSpec.configuration.flaky_test_callback
135
+ example.example_group_instance.instance_exec(example, &RSpec.configuration.flaky_test_callback)
136
+ end
137
+
138
+ if flaky_spec_detection?(attempts)
139
+ if display_try_failure_messages?
140
+ display_try_failure_message(example, attempts, retry_count)
141
+ end
142
+ example.exception = example.metadata[:retry_exceptions].last
143
+ end
144
+ end
145
+ end
146
+
125
147
  self.attempts += 1
126
148
 
127
149
  break if example.exception.nil?
128
150
 
129
151
  example.metadata[:retry_exceptions] << example.exception
130
152
 
131
- break if attempts >= retry_count + 1
153
+ break if !flaky_spec_detection?(attempts) && attempts >= retry_count + 1
132
154
 
133
155
  if exceptions_to_hard_fail.any?
134
156
  break if exception_exists_in?(exceptions_to_hard_fail, example.exception)
@@ -139,17 +161,7 @@ module RSpec
139
161
  end
140
162
 
141
163
  if verbose_retry? && display_try_failure_messages?
142
- if attempts != retry_count + 1
143
- exception_strings =
144
- if ::RSpec::Core::MultipleExceptionError::InterfaceTag === example.exception
145
- example.exception.all_exceptions.map(&:to_s)
146
- else
147
- [example.exception.to_s]
148
- end
149
-
150
- try_message = "\n#{ordinalize(attempts)} Try error in #{example.location}:\n#{exception_strings.join "\n"}\n"
151
- RSpec.configuration.reporter.message(try_message)
152
- end
164
+ display_try_failure_message(example, attempts, retry_count)
153
165
  end
154
166
 
155
167
  example.example_group_instance.clear_lets if clear_lets
@@ -159,6 +171,12 @@ module RSpec
159
171
  example.example_group_instance.instance_exec(example, &RSpec.configuration.retry_callback)
160
172
  end
161
173
 
174
+ if RSpec.configuration.flaky_spec_detection
175
+ if attempts > 0 && example.exception.nil?
176
+ example.example_group_instance.instance_exec(example, &RSpec.configuration.flaky_spec_detection)
177
+ end
178
+ end
179
+
162
180
  sleep sleep_interval if sleep_interval.to_f > 0
163
181
  end
164
182
  end
@@ -184,6 +202,24 @@ module RSpec
184
202
  exception.is_a?(exception_klass) || exception_klass === exception
185
203
  end
186
204
  end
205
+
206
+ def flaky_spec_detection?(attempts)
207
+ RSpec.configuration.flaky_spec_detection? && attempts == 1
208
+ end
209
+
210
+ def display_try_failure_message(example, attempts, retry_count)
211
+ return if attempts == retry_count + 1
212
+
213
+ exception_strings =
214
+ if ::RSpec::Core::MultipleExceptionError::InterfaceTag === example.exception
215
+ example.exception.all_exceptions.map(&:to_s)
216
+ else
217
+ [example.exception.to_s]
218
+ end
219
+
220
+ try_message = "\n#{ordinalize(attempts)} Try error in #{example.location}:\n#{exception_strings.join "\n"}\n"
221
+ RSpec.configuration.reporter.message(try_message)
222
+ end
187
223
  end
188
224
  end
189
225
 
@@ -19,4 +19,5 @@ Gem::Specification.new do |gem|
19
19
  gem.version = RSpec::Rebound::VERSION
20
20
  gem.add_runtime_dependency "rspec-core", "~> 3.3"
21
21
  gem.add_development_dependency "rspec", "~> 3.3"
22
+ gem.add_development_dependency "debug", "~> 1.0"
22
23
  end
@@ -26,7 +26,7 @@ describe RSpec::Rebound do
26
26
  class OtherError < StandardError; end
27
27
  class SharedError < StandardError; end
28
28
  before(:all) do
29
- ENV.delete('RSPEC_RETRY_RETRY_COUNT')
29
+ ENV.delete('RSPEC_REBOUND_RETRY_COUNT')
30
30
  end
31
31
 
32
32
  context 'no retry option' do
@@ -81,14 +81,14 @@ describe RSpec::Rebound do
81
81
  end
82
82
  end
83
83
 
84
- context 'with the environment variable RSPEC_RETRY_RETRY_COUNT' do
84
+ context 'with the environment variable RSPEC_REBOUND_RETRY_COUNT' do
85
85
  before(:all) do
86
86
  set_expectations([false, false, true])
87
- ENV['RSPEC_RETRY_RETRY_COUNT'] = '3'
87
+ ENV['RSPEC_REBOUND_RETRY_COUNT'] = '3'
88
88
  end
89
89
 
90
90
  after(:all) do
91
- ENV.delete('RSPEC_RETRY_RETRY_COUNT')
91
+ ENV.delete('RSPEC_REBOUND_RETRY_COUNT')
92
92
  end
93
93
 
94
94
  it 'should override the retry count set in an example', :retry => 2 do
@@ -378,6 +378,85 @@ describe RSpec::Rebound do
378
378
  end
379
379
  end
380
380
 
381
+ describe 'Flaky callback detection' do
382
+ let!(:example_group) do
383
+ RSpec.describe do
384
+ class ReboundResults
385
+ @@results = {}
386
+ @@flaky_test_callback_called = nil
387
+
388
+ def self.results
389
+ @@results
390
+ end
391
+
392
+ def self.flaky_test_callback_called
393
+ @@flaky_test_callback_called
394
+ end
395
+
396
+ def add(example)
397
+ @@results[example.description] = [example.exception.nil?, example.attempts]
398
+ end
399
+ end
400
+
401
+ def count
402
+ @count ||= 0
403
+ @count
404
+ end
405
+
406
+ def count_up
407
+ @count ||= 0
408
+ @count += 1
409
+ end
410
+
411
+ def set_expectations(expectations)
412
+ @expectations = expectations
413
+ end
414
+
415
+ def shift_expectation
416
+ @expectations.shift
417
+ end
418
+
419
+ before(:all) do
420
+ RSpec.configuration.flaky_test_callback = proc do |example|
421
+ ReboundResults.class_variable_set(:@@flaky_test_callback_called, example.description)
422
+ end
423
+ end
424
+
425
+ after(:all) do
426
+ RSpec.configuration.flaky_test_callback = nil
427
+ end
428
+
429
+ around do |example|
430
+ example.run_with_retry
431
+ results = ReboundResults.results
432
+ results[example.description] = [example.exception.nil?, example.attempts]
433
+ ReboundResults.class_variable_set(:@@results, results)
434
+ end
435
+
436
+ before(:all) do
437
+ set_expectations([false, true])
438
+ end
439
+
440
+ specify 'without retry option', retry: 0 do
441
+ expect(2).to eq(count)
442
+ end
443
+
444
+ specify 'with retry option', retry: 1 do
445
+ expect(true).to be(shift_expectation)
446
+ end
447
+ end
448
+ end
449
+
450
+ it 'should be exposed' do
451
+ example_group.run
452
+ expect(ReboundResults.results).to eq({
453
+ 'without retry option' => [false, 1],
454
+ 'with retry option' => [true, 2]
455
+ })
456
+ expect(ReboundResults.flaky_test_callback_called).to eq("with retry option")
457
+ end
458
+ end
459
+
381
460
  describe 'output in verbose mode' do
382
461
 
383
462
  line_1 = __LINE__ + 8
data/spec/spec_helper.rb CHANGED
@@ -2,10 +2,8 @@ require 'rspec'
2
2
  require 'rspec/core/sandbox'
3
3
 
4
4
  require 'rspec/rebound'
5
- if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2')
6
- require "pry-debugger"
7
- else
8
- require "pry-byebug"
5
+ if Gem::Version.new(RUBY_VERSION) > Gem::Version.new('3')
6
+ require 'debug'
9
7
  end
10
8
 
11
9
  RSpec.configure do |config|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-rebound
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Federico Aldunate
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2025-03-23 00:00:00.000000000 Z
12
+ date: 2025-04-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec-core
@@ -39,6 +39,20 @@ dependencies:
39
39
  - - "~>"
40
40
  - !ruby/object:Gem::Version
41
41
  version: '3.3'
42
+ - !ruby/object:Gem::Dependency
43
+ name: debug
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '1.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '1.0'
42
56
  description: A RSpec extension that automatically retries intermittently failing examples
43
57
  to reduce test flakiness and improve reliability in your test suite.
44
58
  email: