test-prof 0.11.1 → 0.12.2
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 +4 -4
- data/CHANGELOG.md +109 -448
- data/LICENSE.txt +1 -1
- data/README.md +9 -13
- data/config/default.yml +3 -3
- data/lib/minitest/test_prof_plugin.rb +3 -0
- data/lib/test_prof/before_all.rb +0 -4
- data/lib/test_prof/cops/inject.rb +16 -14
- data/lib/test_prof/cops/rspec/aggregate_examples.rb +2 -2
- data/lib/test_prof/cops/rspec/aggregate_examples/its.rb +1 -1
- data/lib/test_prof/cops/rspec/aggregate_examples/line_range_helpers.rb +1 -1
- data/lib/test_prof/cops/rspec/aggregate_examples/matchers_with_side_effects.rb +1 -1
- data/lib/test_prof/cops/rspec/aggregate_examples/metadata_helpers.rb +1 -1
- data/lib/test_prof/cops/rspec/aggregate_examples/node_matchers.rb +1 -1
- data/lib/test_prof/cops/rspec/aggregate_failures.rb +2 -4
- data/lib/test_prof/event_prof/instrumentations/active_support.rb +22 -4
- data/lib/test_prof/ext/active_record_3.rb +1 -1
- data/lib/test_prof/recipes/minitest/before_all.rb +18 -13
- data/lib/test_prof/recipes/minitest/sample.rb +6 -10
- data/lib/test_prof/recipes/rspec/before_all.rb +1 -9
- data/lib/test_prof/recipes/rspec/let_it_be.rb +108 -11
- data/lib/test_prof/recipes/rspec/sample.rb +4 -2
- data/lib/test_prof/stack_prof.rb +3 -0
- data/lib/test_prof/version.rb +1 -1
- metadata +15 -15
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
[](
|
2
|
-
[](https://rubygems.org/gems/test-prof) [](https://cultofmartians.com)
|
2
|
+
[](https://rubygems.org/gems/test-prof) [](https://github.com/test-prof/test-prof/actions)
|
3
|
+
[](https://github.com/test-prof/test-prof/actions)
|
4
|
+
[](https://www.codetriage.com/test-prof/test-prof)
|
5
5
|
[](https://test-prof.evilmartians.io)
|
6
6
|
|
7
7
|
# Ruby Tests Profiling Toolbox
|
@@ -47,11 +47,11 @@ TestProf toolbox aims to help you identify bottlenecks in your test suite. It co
|
|
47
47
|
## Who uses TestProf
|
48
48
|
|
49
49
|
- [Discourse](https://github.com/discourse/discourse) reduced [~27% of their test suite time](https://twitter.com/samsaffron/status/1125602558024699904)
|
50
|
-
- [Gitlab](https://gitlab.com/gitlab-org/gitlab-ce) reduced [39% of their API tests time](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14370)
|
50
|
+
- [Gitlab](https://gitlab.com/gitlab-org/gitlab-ce) reduced [39% of their API tests time](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14370) and [improved factories usage](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26810)
|
51
51
|
- [CodeTriage](https://github.com/codetriage/codetriage)
|
52
52
|
- [Dev.to](https://github.com/thepracticaldev/dev.to)
|
53
53
|
- [Open Project](https://github.com/opf/openproject)
|
54
|
-
- [...and others](https://github.com/
|
54
|
+
- [...and others](https://github.com/test-prof/test-prof/issues/73)
|
55
55
|
|
56
56
|
## Resources
|
57
57
|
|
@@ -83,7 +83,7 @@ And that's it)
|
|
83
83
|
|
84
84
|
Supported Ruby versions:
|
85
85
|
|
86
|
-
- Ruby (MRI) >= 2.
|
86
|
+
- Ruby (MRI) >= 2.5.0 (**NOTE:** for Ruby 2.2 use TestProf < 0.7.0, Ruby 2.3 use TestProf ~> 0.7.0, Ruby 2.4 use TestProf <0.12.0)
|
87
87
|
|
88
88
|
- JRuby >= 9.1.0.0 (**NOTE:** refinements-dependent features might require 9.2.7+)
|
89
89
|
|
@@ -95,16 +95,12 @@ Check out our [docs][].
|
|
95
95
|
|
96
96
|
## What's next?
|
97
97
|
|
98
|
-
Have an idea? [Propose](https://github.com/
|
98
|
+
Have an idea? [Propose](https://github.com/test-prof/test-prof/issues/new) a feature request!
|
99
99
|
|
100
|
-
Already using TestProf? [Share your story!](https://github.com/
|
100
|
+
Already using TestProf? [Share your story!](https://github.com/test-prof/test-prof/issues/73)
|
101
101
|
|
102
102
|
## License
|
103
103
|
|
104
104
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
105
105
|
|
106
106
|
[docs]: https://test-prof.evilmartians.io
|
107
|
-
|
108
|
-
## Security Contact
|
109
|
-
|
110
|
-
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
|
data/config/default.yml
CHANGED
@@ -6,7 +6,7 @@ AllCops:
|
|
6
6
|
- "(?:^|/)spec/"
|
7
7
|
|
8
8
|
RSpec/AggregateExamples:
|
9
|
-
Description: Checks if example
|
9
|
+
Description: Checks if example group contains two or more aggregatable examples.
|
10
10
|
Enabled: true
|
11
11
|
StyleGuide: https://rspec.rubystyle.guide/#expectation-per-example
|
12
12
|
AddAggregateFailuresMetadata: true
|
@@ -21,8 +21,8 @@ RSpec/AggregateExamples:
|
|
21
21
|
|
22
22
|
# TODO: remove this one we hit 1.0
|
23
23
|
RSpec/AggregateFailures:
|
24
|
-
Description: Checks if example
|
25
|
-
Enabled:
|
24
|
+
Description: Checks if example group contains two or more aggregatable examples.
|
25
|
+
Enabled: false
|
26
26
|
StyleGuide: https://rspec.rubystyle.guide/#expectation-per-example
|
27
27
|
AddAggregateFailuresMetadata: true
|
28
28
|
MatchersWithSideEffects:
|
@@ -12,6 +12,7 @@ module Minitest # :nodoc:
|
|
12
12
|
opts[:top_count] = ENV["EVENT_PROF_TOP"].to_i if ENV["EVENT_PROF_TOP"]
|
13
13
|
opts[:per_example] = true if ENV["EVENT_PROF_EXAMPLES"]
|
14
14
|
opts[:fdoc] = true if ENV["FDOC"]
|
15
|
+
opts[:sample] = true if ENV["SAMPLE"] || ENV["SAMPLE_GROUPS"]
|
15
16
|
end
|
16
17
|
end
|
17
18
|
end
|
@@ -39,5 +40,7 @@ module Minitest # :nodoc:
|
|
39
40
|
|
40
41
|
reporter << TestProf::EventProfReporter.new(options[:io], options) if options[:event]
|
41
42
|
reporter << TestProf::FactoryDoctorReporter.new(options[:io], options) if options[:fdoc]
|
43
|
+
|
44
|
+
::TestProf::MinitestSample.call if options[:sample]
|
42
45
|
end
|
43
46
|
end
|
data/lib/test_prof/before_all.rb
CHANGED
@@ -2,22 +2,24 @@
|
|
2
2
|
|
3
3
|
# This is shamelessly borrowed from RuboCop RSpec
|
4
4
|
# https://github.com/rubocop-hq/rubocop-rspec/blob/master/lib/rubocop/rspec/inject.rb
|
5
|
-
module
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
module TestProf
|
6
|
+
module Cops
|
7
|
+
# Because RuboCop doesn't yet support plugins, we have to monkey patch in a
|
8
|
+
# bit of our configuration.
|
9
|
+
module Inject
|
10
|
+
PROJECT_ROOT = Pathname.new(__dir__).parent.parent.parent.expand_path.freeze
|
11
|
+
CONFIG_DEFAULT = PROJECT_ROOT.join("config", "default.yml").freeze
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
def self.defaults!
|
14
|
+
path = CONFIG_DEFAULT.to_s
|
15
|
+
hash = RuboCop::ConfigLoader.send(:load_yaml_configuration, path)
|
16
|
+
config = RuboCop::Config.new(hash, path)
|
17
|
+
puts "configuration from #{path}" if RuboCop::ConfigLoader.debug?
|
18
|
+
config = RuboCop::ConfigLoader.merge_with_default(config, path)
|
19
|
+
RuboCop::ConfigLoader.instance_variable_set(:@default_configuration, config)
|
20
|
+
end
|
19
21
|
end
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
23
|
-
|
25
|
+
TestProf::Cops::Inject.defaults!
|
@@ -12,7 +12,7 @@ module RuboCop
|
|
12
12
|
module RSpec
|
13
13
|
# Checks if example groups contain two or more aggregatable examples.
|
14
14
|
#
|
15
|
-
# @see https://github.com/rubocop-hq/rspec-style-guide#
|
15
|
+
# @see https://github.com/rubocop-hq/rspec-style-guide#expectation-per-example
|
16
16
|
#
|
17
17
|
# This cop is primarily for reducing the cost of repeated expensive
|
18
18
|
# context initialization.
|
@@ -108,7 +108,7 @@ module RuboCop
|
|
108
108
|
# expect(number).to be_odd
|
109
109
|
# end
|
110
110
|
#
|
111
|
-
class AggregateExamples < Cop
|
111
|
+
class AggregateExamples < ::RuboCop::Cop::Cop
|
112
112
|
include LineRangeHelpers
|
113
113
|
include MetadataHelpers
|
114
114
|
include NodeMatchers
|
@@ -5,7 +5,7 @@ require_relative "../language"
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
7
7
|
module RSpec
|
8
|
-
class AggregateExamples < Cop
|
8
|
+
class AggregateExamples < ::RuboCop::Cop::Cop
|
9
9
|
# When aggregated, the expectations will fail when not supposed to or
|
10
10
|
# have a risk of not failing when expected to. One example is
|
11
11
|
# `validate_presence_of :comment` as it leaves an empty comment after
|
@@ -4,14 +4,12 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module RSpec
|
6
6
|
class AggregateExamples
|
7
|
-
def self.
|
8
|
-
|
7
|
+
def self.registry
|
8
|
+
RuboCop::Cop::Cop.registry
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
12
|
class AggregateFailures < AggregateExamples
|
13
|
-
raise "Remove me" if TestProf::VERSION >= "1.0"
|
14
|
-
|
15
13
|
def initialize(*)
|
16
14
|
super
|
17
15
|
self.class.just_once { warn "`AggregateFailures` cop has been renamed to `AggregateExamples`." }
|
@@ -4,13 +4,31 @@ module TestProf::EventProf
|
|
4
4
|
module Instrumentations
|
5
5
|
# Wrapper over ActiveSupport::Notifications
|
6
6
|
module ActiveSupport
|
7
|
+
class Subscriber
|
8
|
+
attr_reader :block, :started_at
|
9
|
+
|
10
|
+
def initialize(block)
|
11
|
+
@block = block
|
12
|
+
end
|
13
|
+
|
14
|
+
def start(*)
|
15
|
+
@started_at = TestProf.now
|
16
|
+
end
|
17
|
+
|
18
|
+
def publish(_name, started_at, finished_at, *)
|
19
|
+
block.call(finished_at - started_at)
|
20
|
+
end
|
21
|
+
|
22
|
+
def finish(*)
|
23
|
+
block.call(TestProf.now - started_at)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
7
27
|
class << self
|
8
|
-
def subscribe(event)
|
28
|
+
def subscribe(event, &block)
|
9
29
|
raise ArgumentError, "Block is required!" unless block_given?
|
10
30
|
|
11
|
-
::ActiveSupport::Notifications.subscribe(event
|
12
|
-
yield (finish - start)
|
13
|
-
end
|
31
|
+
::ActiveSupport::Notifications.subscribe(event, Subscriber.new(block))
|
14
32
|
end
|
15
33
|
|
16
34
|
def instrument(event)
|
@@ -5,8 +5,8 @@ module TestProf
|
|
5
5
|
module ActiveRecord3Transactions
|
6
6
|
refine ::ActiveRecord::ConnectionAdapters::AbstractAdapter do
|
7
7
|
def begin_transaction(joinable: true)
|
8
|
-
increment_open_transactions
|
9
8
|
if open_transactions > 0
|
9
|
+
increment_open_transactions
|
10
10
|
create_savepoint
|
11
11
|
else
|
12
12
|
begin_db_transaction
|
@@ -8,20 +8,21 @@ module TestProf
|
|
8
8
|
# store instance variables
|
9
9
|
module Minitest # :nodoc: all
|
10
10
|
class Executor
|
11
|
-
attr_reader :active
|
11
|
+
attr_reader :active, :block, :captured_ivars
|
12
12
|
|
13
13
|
alias active? active
|
14
14
|
|
15
15
|
def initialize(&block)
|
16
16
|
@block = block
|
17
|
+
@captured_ivars = []
|
17
18
|
end
|
18
19
|
|
19
|
-
def activate!(
|
20
|
-
return if active?
|
20
|
+
def activate!(test_object)
|
21
|
+
return restore_ivars(test_object) if active?
|
21
22
|
@active = true
|
22
|
-
@examples_left =
|
23
|
+
@examples_left = test_object.class.runnable_methods.size
|
23
24
|
BeforeAll.begin_transaction do
|
24
|
-
capture!
|
25
|
+
capture!(test_object)
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
@@ -33,16 +34,21 @@ module TestProf
|
|
33
34
|
BeforeAll.rollback_transaction
|
34
35
|
end
|
35
36
|
|
36
|
-
def capture!
|
37
|
-
|
37
|
+
def capture!(test_object)
|
38
|
+
before_ivars = test_object.instance_variables
|
39
|
+
|
40
|
+
test_object.instance_eval(&block)
|
41
|
+
|
42
|
+
(test_object.instance_variables - before_ivars).each do |ivar|
|
43
|
+
captured_ivars << [ivar, test_object.instance_variable_get(ivar)]
|
44
|
+
end
|
38
45
|
end
|
39
46
|
|
40
|
-
def
|
41
|
-
|
42
|
-
next if ivar == :@block
|
47
|
+
def restore_ivars(test_object)
|
48
|
+
captured_ivars.each do |(ivar, val)|
|
43
49
|
test_object.instance_variable_set(
|
44
50
|
ivar,
|
45
|
-
|
51
|
+
val
|
46
52
|
)
|
47
53
|
end
|
48
54
|
end
|
@@ -62,8 +68,7 @@ module TestProf
|
|
62
68
|
|
63
69
|
prepend(Module.new do
|
64
70
|
def setup
|
65
|
-
self.class.before_all_executor.activate!(self
|
66
|
-
self.class.before_all_executor.restore_to(self)
|
71
|
+
self.class.before_all_executor.activate!(self)
|
67
72
|
super
|
68
73
|
end
|
69
74
|
|
@@ -35,18 +35,14 @@ module TestProf
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
38
|
-
end
|
39
38
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
39
|
+
def call
|
40
|
+
if ENV["SAMPLE"]
|
41
|
+
::TestProf::MinitestSample.sample_examples(ENV["SAMPLE"].to_i)
|
42
|
+
elsif ENV["SAMPLE_GROUPS"]
|
43
|
+
::TestProf::MinitestSample.sample_groups(ENV["SAMPLE_GROUPS"].to_i)
|
44
|
+
end
|
46
45
|
end
|
47
|
-
super
|
48
46
|
end
|
49
47
|
end
|
50
48
|
end
|
51
|
-
|
52
|
-
Minitest.singleton_class.prepend(TestProf::MinitestSample)
|
@@ -9,7 +9,7 @@ module TestProf
|
|
9
9
|
def before_all(&block)
|
10
10
|
raise ArgumentError, "Block is required!" unless block_given?
|
11
11
|
|
12
|
-
return
|
12
|
+
return before(:all, &block) if within_before_all?
|
13
13
|
|
14
14
|
@__before_all_activated__ = true
|
15
15
|
|
@@ -24,14 +24,6 @@ module TestProf
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
def within_before_all(&block)
|
28
|
-
before(:all) do
|
29
|
-
BeforeAll.within_transaction do
|
30
|
-
instance_eval(&block)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
27
|
def within_before_all?
|
36
28
|
instance_variable_defined?(:@__before_all_activated__)
|
37
29
|
end
|
@@ -22,6 +22,10 @@ module TestProf
|
|
22
22
|
|
23
23
|
LetItBe.modifiers[key] = block
|
24
24
|
end
|
25
|
+
|
26
|
+
def default_modifiers
|
27
|
+
@default_modifiers ||= {}
|
28
|
+
end
|
25
29
|
end
|
26
30
|
|
27
31
|
class << self
|
@@ -75,6 +79,8 @@ module TestProf
|
|
75
79
|
# And we love cats!)
|
76
80
|
PREFIX = RUBY_ENGINE == "jruby" ? "@__jruby_is_not_cat_friendly__" : "@😸"
|
77
81
|
|
82
|
+
FROZEN_ERROR_HINT = "\nIf you are using `let_it_be`, you may want to pass `reload: true` or `refind: true` modifier to it."
|
83
|
+
|
78
84
|
def self.define_let_it_be_alias(name, **default_args)
|
79
85
|
define_method(name) do |identifier, **options, &blk|
|
80
86
|
let_it_be(identifier, **default_args.merge(options), &blk)
|
@@ -83,20 +89,20 @@ module TestProf
|
|
83
89
|
|
84
90
|
def let_it_be(identifier, **options, &block)
|
85
91
|
initializer = proc do
|
86
|
-
instance_variable_set(:"#{PREFIX}#{identifier}", instance_exec(&block))
|
92
|
+
instance_variable_set(:"#{TestProf::LetItBe::PREFIX}#{identifier}", instance_exec(&block))
|
93
|
+
rescue FrozenError => e
|
94
|
+
e.message << TestProf::LetItBe::FROZEN_ERROR_HINT
|
95
|
+
raise
|
87
96
|
end
|
88
97
|
|
89
|
-
|
90
|
-
|
91
|
-
else
|
92
|
-
before_all(&initializer)
|
93
|
-
end
|
98
|
+
default_options = LetItBe.config.default_modifiers.dup
|
99
|
+
default_options.merge!(metadata[:let_it_be_modifiers]) if metadata[:let_it_be_modifiers]
|
94
100
|
|
95
|
-
|
96
|
-
|
101
|
+
options = default_options.merge(options)
|
102
|
+
|
103
|
+
before_all(&initializer)
|
97
104
|
|
98
|
-
|
99
|
-
let_accessor = LetItBe.wrap_with_modifiers(modifiers) do
|
105
|
+
let_accessor = LetItBe.wrap_with_modifiers(options) do
|
100
106
|
instance_variable_get(:"#{PREFIX}#{identifier}")
|
101
107
|
end
|
102
108
|
|
@@ -114,16 +120,78 @@ module TestProf
|
|
114
120
|
|
115
121
|
let(identifier, &let_accessor)
|
116
122
|
end
|
123
|
+
|
124
|
+
module Freezer
|
125
|
+
# Stoplist to prevent freezing objects and theirs associations that are defined
|
126
|
+
# with `let_it_be`'s `freeze: false` options during deep freezing.
|
127
|
+
#
|
128
|
+
# To only keep track of objects that are available in current example group,
|
129
|
+
# `begin` adds a new layer, and `rollback` removes a layer of unrelated objects
|
130
|
+
# along with rolling back the transaction where they were created.
|
131
|
+
#
|
132
|
+
# Stoplist holds records declared with `freeze: false` (so we do not freeze them even if they're used as
|
133
|
+
# associated records for frozen objects)
|
134
|
+
module Stoplist
|
135
|
+
class << self
|
136
|
+
def stop?(record)
|
137
|
+
@stoplist.any? { |layer| layer.include?(record) }
|
138
|
+
end
|
139
|
+
|
140
|
+
def stop!(record)
|
141
|
+
@stoplist.last.push(record)
|
142
|
+
end
|
143
|
+
|
144
|
+
def begin
|
145
|
+
@stoplist.push([])
|
146
|
+
end
|
147
|
+
|
148
|
+
def rollback
|
149
|
+
@stoplist.pop
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Stack of example group-related variable definitions
|
154
|
+
@stoplist = []
|
155
|
+
end
|
156
|
+
|
157
|
+
class << self
|
158
|
+
# Rerucsively freezes the object to detect modifications
|
159
|
+
def deep_freeze(record)
|
160
|
+
return if record.frozen?
|
161
|
+
return if Stoplist.stop?(record)
|
162
|
+
|
163
|
+
record.freeze
|
164
|
+
|
165
|
+
# Support `let_it_be` with `create_list`
|
166
|
+
return record.each { |rec| deep_freeze(rec) } if record.respond_to?(:each)
|
167
|
+
|
168
|
+
# Freeze associations as well.
|
169
|
+
return unless defined?(::ActiveRecord::Base)
|
170
|
+
return unless record.is_a?(::ActiveRecord::Base)
|
171
|
+
|
172
|
+
record.class.reflections.keys.each do |reflection|
|
173
|
+
# But only if they are already loaded. If not yet loaded, they weren't
|
174
|
+
# created by factories, and it's ok to mutate them.
|
175
|
+
|
176
|
+
next unless record.association(reflection.to_sym).loaded?
|
177
|
+
|
178
|
+
target = record.association(reflection.to_sym).target
|
179
|
+
deep_freeze(target) if target.is_a?(::ActiveRecord::Base) || target.respond_to?(:each)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
117
184
|
end
|
118
185
|
end
|
119
186
|
|
120
|
-
if defined?(::ActiveRecord)
|
187
|
+
if defined?(::ActiveRecord::Base)
|
121
188
|
require "test_prof/ext/active_record_refind"
|
122
189
|
using TestProf::Ext::ActiveRecordRefind
|
123
190
|
|
124
191
|
TestProf::LetItBe.configure do |config|
|
125
192
|
config.register_modifier :reload do |record, val|
|
126
193
|
next record unless val
|
194
|
+
|
127
195
|
next record.reload if record.is_a?(::ActiveRecord::Base)
|
128
196
|
|
129
197
|
if record.respond_to?(:map)
|
@@ -136,6 +204,7 @@ if defined?(::ActiveRecord)
|
|
136
204
|
|
137
205
|
config.register_modifier :refind do |record, val|
|
138
206
|
next record unless val
|
207
|
+
|
139
208
|
next record.refind if record.is_a?(::ActiveRecord::Base)
|
140
209
|
|
141
210
|
if record.respond_to?(:map)
|
@@ -145,7 +214,35 @@ if defined?(::ActiveRecord)
|
|
145
214
|
end
|
146
215
|
record
|
147
216
|
end
|
217
|
+
|
218
|
+
config.register_modifier :freeze do |record, val|
|
219
|
+
if val == false
|
220
|
+
TestProf::LetItBe::Freezer::Stoplist.stop!(record)
|
221
|
+
next record
|
222
|
+
end
|
223
|
+
|
224
|
+
TestProf::LetItBe::Freezer.deep_freeze(record)
|
225
|
+
record
|
226
|
+
end
|
148
227
|
end
|
149
228
|
end
|
150
229
|
|
151
230
|
RSpec::Core::ExampleGroup.extend TestProf::LetItBe
|
231
|
+
|
232
|
+
TestProf::BeforeAll.configure do |config|
|
233
|
+
config.before(:begin) do
|
234
|
+
TestProf::LetItBe::Freezer::Stoplist.begin
|
235
|
+
end
|
236
|
+
|
237
|
+
config.after(:rollback) do
|
238
|
+
TestProf::LetItBe::Freezer::Stoplist.rollback
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
RSpec.configure do |config|
|
243
|
+
config.after(:example) do |example|
|
244
|
+
if example.exception&.is_a?(FrozenError)
|
245
|
+
example.exception.message << TestProf::LetItBe::FROZEN_ERROR_HINT
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|