Skip to content

Commit 1f9a487

Browse files
committed
Add configurable error comparison
1 parent 1771ccb commit 1f9a487

4 files changed

Lines changed: 61 additions & 15 deletions

File tree

lib/scientist/experiment.rb

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,16 @@ def compare(*args, &block)
121121
@_scientist_comparator = block
122122
end
123123

124+
# A block which compares two experimental errors.
125+
#
126+
# The block must take two arguments, the control Error and a candidate Error,
127+
# and return true or false.
128+
#
129+
# Returns the block.
130+
def error_compare(*args, &block)
131+
@_scientist_error_comparator = block
132+
end
133+
124134
# A Symbol-keyed Hash of extra experiment data.
125135
def context(context = nil)
126136
@_scientist_context ||= {}
@@ -164,13 +174,9 @@ def name
164174
"experiment"
165175
end
166176

167-
# Internal: compare two observations, using the configured compare block if present.
177+
# Internal: compare two observations, using the configured compare and error_compare lambdas if present.
168178
def observations_are_equivalent?(a, b)
169-
if @_scientist_comparator
170-
a.equivalent_to?(b, &@_scientist_comparator)
171-
else
172-
a.equivalent_to? b
173-
end
179+
a.equivalent_to? b, @_scientist_comparator, @_scientist_error_comparator
174180
rescue StandardError => ex
175181
raised :compare, ex
176182
false

lib/scientist/observation.rb

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,25 +47,34 @@ def cleaned_value
4747

4848
# Is this observation equivalent to another?
4949
#
50-
# other - the other Observation in question
51-
# comparator - an optional comparison block. This observation's value and the
52-
# other observation's value are yielded to this to determine
53-
# their equivalency. Block should return true/false.
50+
# other - the other Observation in question
51+
# comparator - an optional comparison proc. This observation's value and the
52+
# other observation's value are passed to this to determine
53+
# their equivalency. Proc should return true/false.
54+
# error_comparator - an optional comparison proc. This observation's Error and the
55+
# other observation's Error are passed to this to determine
56+
# their equivalency. Proc should return true/false.
5457
#
5558
# Returns true if:
5659
#
5760
# * The values of the observation are equal (using `==`)
5861
# * The values of the observations are equal according to a comparison
59-
# block, if given
62+
# prod, if given
63+
# * The exceptions raised by the obeservations are equal according to the
64+
# error comparison proc, if given.
6065
# * Both observations raised an exception with the same class and message.
6166
#
6267
# Returns false otherwise.
63-
def equivalent_to?(other, &comparator)
68+
def equivalent_to?(other, comparator=nil, error_comparator=nil)
6469
return false unless other.is_a?(Scientist::Observation)
6570

6671
if raised? || other.raised?
67-
return other.exception.class == exception.class &&
68-
other.exception.message == exception.message
72+
if error_comparator
73+
error_comparator.call(exception, other.exception)
74+
else
75+
return other.exception.class == exception.class &&
76+
other.exception.message == exception.message
77+
end
6978
end
7079

7180
if comparator

test/scientist/experiment_test.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,18 @@ def @ex.publish(result)
180180
assert @ex.published_result.matched?
181181
end
182182

183+
it "compares errors with an error comparator block if provided" do
184+
@ex.error_compare { |a, b| a.class == b.class }
185+
@ex.use { raise "foo" }
186+
@ex.try { raise "bar" }
187+
188+
resulting_error = assert_raises RuntimeError do
189+
@ex.run
190+
end
191+
assert_equal "foo", resulting_error.message
192+
assert @ex.published_result.matched?
193+
end
194+
183195
it "knows how to compare two experiments" do
184196
a = Scientist::Observation.new(@ex, "a") { 1 }
185197
b = Scientist::Observation.new(@ex, "b") { 2 }

test/scientist/observation_test.rb

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
refute x.equivalent_to?(y)
9393
end
9494

95-
it "compares values using a comparator block" do
95+
it "compares values using a comparator proc" do
9696
a = Scientist::Observation.new("test", @experiment) { 1 }
9797
b = Scientist::Observation.new("test", @experiment) { "1" }
9898

@@ -108,6 +108,25 @@
108108
assert_equal [a.value, b.value], yielded
109109
end
110110

111+
it "compares exceptions using an error comparator proc" do
112+
x = Scientist::Observation.new("test", @experiment) { raise FirstError, "error" }
113+
y = Scientist::Observation.new("test", @experiment) { raise SecondError, "error" }
114+
z = Scientist::Observation.new("test", @experiment) { raise FirstError, "ERROR" }
115+
116+
refute x.equivalent_to?(z)
117+
refute x.equivalent_to?(y)
118+
119+
compare_on_class = -> (error, other_error) {
120+
error.class == other_error.class
121+
}
122+
compare_on_message = -> (error, other_error) {
123+
error.message == other_error.message
124+
}
125+
126+
assert x.equivalent_to?(z, nil, compare_on_class)
127+
assert x.equivalent_to?(y, nil, compare_on_message)
128+
end
129+
111130
describe "#cleaned_value" do
112131
it "returns the observation's value by default" do
113132
a = Scientist::Observation.new("test", @experiment) { 1 }

0 commit comments

Comments
 (0)