Skip to content

Commit

Permalink
record result of invocation before 'returning' it
Browse files Browse the repository at this point in the history
This helps us avoid rescuing an exception raised by code elsewhere in the Mocha
code. Also, enables us to handle the missed scenario where Expectaction#throws
is called, 'coz without it, we'd need a generic way to catch any thrown tag and
that might be quite awkward.
  • Loading branch information
nitishr committed Nov 6, 2019
1 parent 0d57961 commit 9007a97
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 42 deletions.
3 changes: 2 additions & 1 deletion lib/mocha/exception_raiser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ def initialize(exception, message)
@message = message
end

def evaluate
def evaluate(invocation)
invocation.raised(@exception)
raise @exception, @exception.to_s if @exception.is_a?(Module) && (@exception < Interrupt)
raise @exception, @message if @message
raise @exception
Expand Down
20 changes: 12 additions & 8 deletions lib/mocha/invocation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def initialize(method_name, yield_parameters = YieldParameters.new, return_value
@yield_parameters = yield_parameters
@return_values = return_values
@yields = []
@result = nil
end

# @private
Expand All @@ -20,14 +21,17 @@ def call(*arguments)
@yields << ParametersMatcher.new(yield_parameters)
yield(*yield_parameters)
end
begin
@result = @return_values.next
# rubocop:disable Lint/RescueException
rescue Exception => e
# rubocop:enable Lint/RescueException
@result = RaisedException.new(e)
raise
end
@return_values.next(self)
end

# @private
def returned(value)
@result = value
end

# @private
def raised(exception)
@result = RaisedException.new(exception)
end

# @private
Expand Down
6 changes: 3 additions & 3 deletions lib/mocha/return_values.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ def initialize(*values)
@values = values
end

def next
def next(invocation)
case @values.length
when 0 then nil
when 1 then @values.first.evaluate
else @values.shift.evaluate
when 1 then @values.first.evaluate(invocation)
else @values.shift.evaluate(invocation)
end
end

Expand Down
3 changes: 2 additions & 1 deletion lib/mocha/single_return_value.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ def initialize(value)
@value = value
end

def evaluate
def evaluate(invocation)
invocation.returned(@value)
@value
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/mocha/thrower.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ def initialize(tag, object = nil)
@object = object
end

def evaluate
def evaluate(_invocation)
throw @tag, @object
end
end
Expand Down
15 changes: 10 additions & 5 deletions test/unit/exception_raiser_test.rb
Original file line number Diff line number Diff line change
@@ -1,40 +1,45 @@
require File.expand_path('../../test_helper', __FILE__)

require 'mocha/invocation'
require 'mocha/exception_raiser'
require 'timeout'

class ExceptionRaiserTest < Mocha::TestCase
include Mocha

def new_invocation
Invocation.new(:irrelevant)
end

def test_should_raise_exception_with_specified_class_and_default_message
exception_class = Class.new(StandardError)
raiser = ExceptionRaiser.new(exception_class, nil)
exception = assert_raises(exception_class) { raiser.evaluate }
exception = assert_raises(exception_class) { raiser.evaluate(new_invocation) }
assert_equal exception_class.to_s, exception.message
end

def test_should_raise_exception_with_specified_class_and_message
exception_class = Class.new(StandardError)
raiser = ExceptionRaiser.new(exception_class, 'message')
exception = assert_raises(exception_class) { raiser.evaluate }
exception = assert_raises(exception_class) { raiser.evaluate(new_invocation) }
assert_equal 'message', exception.message
end

def test_should_raise_exception_instance
exception_class = Class.new(StandardError)
raiser = ExceptionRaiser.new(exception_class.new('message'), nil)
exception = assert_raises(exception_class) { raiser.evaluate }
exception = assert_raises(exception_class) { raiser.evaluate(new_invocation) }
assert_equal 'message', exception.message
end

def test_should_raise_interrupt_exception_with_default_message_so_it_works_in_ruby_1_8_6
raiser = ExceptionRaiser.new(Interrupt, nil)
assert_raises(Interrupt) { raiser.evaluate }
assert_raises(Interrupt) { raiser.evaluate(new_invocation) }
end

def test_should_raise_subclass_of_interrupt_exception_with_default_message_so_it_works_in_ruby_1_8_6
exception_class = Class.new(Interrupt)
raiser = ExceptionRaiser.new(exception_class, nil)
assert_raises(exception_class) { raiser.evaluate }
assert_raises(exception_class) { raiser.evaluate(new_invocation) }
end
end
45 changes: 25 additions & 20 deletions test/unit/return_values_test.rb
Original file line number Diff line number Diff line change
@@ -1,61 +1,66 @@
require File.expand_path('../../test_helper', __FILE__)

require 'mocha/invocation'
require 'mocha/return_values'

class ReturnValuesTest < Mocha::TestCase
include Mocha

def new_invocation
Invocation.new(:irrelevant)
end

def test_should_return_nil
values = ReturnValues.new
assert_nil values.next
assert_nil values.next(new_invocation)
end

def test_should_keep_returning_nil
values = ReturnValues.new
values.next
assert_nil values.next
assert_nil values.next
values.next(new_invocation)
assert_nil values.next(new_invocation)
assert_nil values.next(new_invocation)
end

def test_should_return_evaluated_single_return_value
values = ReturnValues.new(SingleReturnValue.new('value'))
assert_equal 'value', values.next
assert_equal 'value', values.next(new_invocation)
end

def test_should_keep_returning_evaluated_single_return_value
values = ReturnValues.new(SingleReturnValue.new('value'))
values.next
assert_equal 'value', values.next
assert_equal 'value', values.next
values.next(new_invocation)
assert_equal 'value', values.next(new_invocation)
assert_equal 'value', values.next(new_invocation)
end

def test_should_return_consecutive_evaluated_single_return_values
values = ReturnValues.new(SingleReturnValue.new('value_1'), SingleReturnValue.new('value_2'))
assert_equal 'value_1', values.next
assert_equal 'value_2', values.next
assert_equal 'value_1', values.next(new_invocation)
assert_equal 'value_2', values.next(new_invocation)
end

def test_should_keep_returning_last_of_consecutive_evaluated_single_return_values
values = ReturnValues.new(SingleReturnValue.new('value_1'), SingleReturnValue.new('value_2'))
values.next
values.next
assert_equal 'value_2', values.next
assert_equal 'value_2', values.next
values.next(new_invocation)
values.next(new_invocation)
assert_equal 'value_2', values.next(new_invocation)
assert_equal 'value_2', values.next(new_invocation)
end

def test_should_build_single_return_values_for_each_values
values = ReturnValues.build('value_1', 'value_2', 'value_3').values
assert_equal 'value_1', values[0].evaluate
assert_equal 'value_2', values[1].evaluate
assert_equal 'value_3', values[2].evaluate
assert_equal 'value_1', values[0].evaluate(new_invocation)
assert_equal 'value_2', values[1].evaluate(new_invocation)
assert_equal 'value_3', values[2].evaluate(new_invocation)
end

def test_should_combine_two_sets_of_return_values
values1 = ReturnValues.build('value_1')
values2 = ReturnValues.build('value_2a', 'value_2b')
values = (values1 + values2).values
assert_equal 'value_1', values[0].evaluate
assert_equal 'value_2a', values[1].evaluate
assert_equal 'value_2b', values[2].evaluate
assert_equal 'value_1', values[0].evaluate(new_invocation)
assert_equal 'value_2a', values[1].evaluate(new_invocation)
assert_equal 'value_2b', values[2].evaluate(new_invocation)
end
end
7 changes: 6 additions & 1 deletion test/unit/single_return_value_test.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
require File.expand_path('../../test_helper', __FILE__)

require 'mocha/invocation'
require 'mocha/single_return_value'

class SingleReturnValueTest < Mocha::TestCase
include Mocha

def new_invocation
Invocation.new(:irrelevant)
end

def test_should_return_value
value = SingleReturnValue.new('value')
assert_equal 'value', value.evaluate
assert_equal 'value', value.evaluate(new_invocation)
end
end
9 changes: 7 additions & 2 deletions test/unit/thrower_test.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
require File.expand_path('../../test_helper', __FILE__)

require 'mocha/invocation'
require 'mocha/thrower'

class ThrowerTest < Mocha::TestCase
include Mocha

def new_invocation
Invocation.new(:irrelevant)
end

def test_should_throw_tag
thrower = Thrower.new(:tag)
assert_throws(:tag) { thrower.evaluate }
assert_throws(:tag) { thrower.evaluate(new_invocation) }
end

def test_should_throw_tag_with_return_value
thrower = Thrower.new(:tag, 'return-value')
return_value = catch(:tag) { thrower.evaluate }
return_value = catch(:tag) { thrower.evaluate(new_invocation) }
assert_equal 'return-value', return_value
end
end

0 comments on commit 9007a97

Please sign in to comment.