diff --git a/lib/mocha/cardinality.rb b/lib/mocha/cardinality.rb index ab9b4f38e..c3efb6126 100644 --- a/lib/mocha/cardinality.rb +++ b/lib/mocha/cardinality.rb @@ -3,27 +3,14 @@ class Cardinality INFINITY = 1 / 0.0 def initialize(required = 0, maximum = INFINITY) - update(required, maximum) + range(required, maximum) @invocations = [] end - def exactly(count) - update(count, count) - end - - def at_least(count) - update(count, INFINITY) - end - - def at_most(count) - update(0, count) - end - - def times(range_or_count) - case range_or_count - when Range then update(range_or_count.first, range_or_count.last) - else update(range_or_count, range_or_count) - end + def range(at_least = 0, at_most = INFINITY) + @required = at_least + @maximum = at_most + self end def <<(invocation) @@ -59,21 +46,21 @@ def anticipated_times if allowed_any_number_of_times? 'allowed any number of times' elsif required.zero? && maximum.zero? - "expected #{count(maximum)}" + "expected #{times(maximum)}" elsif required == maximum - "expected exactly #{count(required)}" + "expected exactly #{times(required)}" elsif infinite?(maximum) - "expected at least #{count(required)}" + "expected at least #{times(required)}" elsif required.zero? - "expected at most #{count(maximum)}" + "expected at most #{times(maximum)}" else - "expected between #{required} and #{count(maximum)}" + "expected between #{required} and #{times(maximum)}" end end # rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity def invoked_times - "invoked #{count(@invocations.size)}" + "invoked #{times(@invocations.size)}" end def actual_invocations @@ -84,7 +71,7 @@ def actual_invocations attr_reader :required, :maximum - def count(number) + def times(number) case number when 0 then 'never' when 1 then 'once' @@ -93,12 +80,6 @@ def count(number) end end - def update(required, maximum) - @required = required - @maximum = maximum - self - end - def infinite?(number) number.respond_to?(:infinite?) && number.infinite? end diff --git a/lib/mocha/expectation.rb b/lib/mocha/expectation.rb index aea03f3bb..49f6b081f 100644 --- a/lib/mocha/expectation.rb +++ b/lib/mocha/expectation.rb @@ -17,7 +17,7 @@ module Mocha class Expectation # Modifies expectation so that the number of calls to the expected method must be within a specific +range+. # - # @param [Range,Integer] range specifies the allowable range in the number of expected invocations. + # @param [Range,Integer] range specifies the allowable number or range in the number of expected invocations. # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. # # @example Specifying a specific number of expected invocations. @@ -42,11 +42,12 @@ class Expectation # object.expected_method # # => verify fails def times(range) - @cardinality.times(range) + range = range..range unless range.is_a?(Range) + @cardinality.range(range.first, range.last) self end - # Modifies expectation so that the expected method must be called exactly twice. + # Modifies expectation so that the expected method must be called exactly twice. Has the same effect as calling {times}(2). # # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. # @@ -68,11 +69,10 @@ def times(range) # object.expected_method # # => verify fails def twice - @cardinality.exactly(2) - self + times(2) end - # Modifies expectation so that the expected method must be called exactly once. + # Modifies expectation so that the expected method must be called exactly once. Has the same effect as calling {times}(1). # # Note that this is the default behaviour for an expectation, but you may wish to use it for clarity/emphasis. # @@ -93,11 +93,10 @@ def twice # object.expects(:expected_method).once # # => verify fails def once - @cardinality.exactly(1) - self + times(1) end - # Modifies expectation so that the expected method must never be called. + # Modifies expectation so that the expected method must never be called. Has the same effect as calling {times}(0). # # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. # @@ -110,11 +109,11 @@ def once # object.expects(:expected_method).never # # => verify succeeds def never - @cardinality.exactly(0) - self + times(0) end - # Modifies expectation so that the expected method must be called at least a +minimum_number_of_times+. + # Modifies expectation so that the expected method must be called at least a +minimum_number_of_times+ and at most any number of times. + # Has the same effect as calling {times}(+minimum_number_of_times+..Float::INFINITY). # # @param [Integer] minimum_number_of_times minimum number of expected invocations. # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. @@ -130,11 +129,12 @@ def never # object.expected_method # # => verify fails def at_least(minimum_number_of_times) - @cardinality.at_least(minimum_number_of_times) + @cardinality.range(minimum_number_of_times) self end - # Modifies expectation so that the expected method must be called at least once. + # Modifies expectation so that the expected method must be called at least once and at most any number of times. + # Has the same effect as calling {at_least}(1). # # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. # @@ -151,7 +151,8 @@ def at_least_once at_least(1) end - # Modifies expectation so that the expected method must be called at most a +maximum_number_of_times+. + # Modifies expectation so that the expected method must be called from never to at most a +maximum_number_of_times+. + # Has the same effect as calling {times}(0..+maximum_number_of_times+). # # @param [Integer] maximum_number_of_times maximum number of expected invocations. # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. @@ -166,11 +167,11 @@ def at_least_once # object.expects(:expected_method).at_most(2) # 3.times { object.expected_method } # => unexpected invocation def at_most(maximum_number_of_times) - @cardinality.at_most(maximum_number_of_times) - self + times(0..maximum_number_of_times) end - # Modifies expectation so that the expected method must be called at most once. + # Modifies expectation so that the expected method must be called from never to at most once. + # Has the same effect as calling {at_most}(1). # # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. # @@ -547,7 +548,7 @@ def initialize(mock, expected_method_name, backtrace = nil) @block_matcher = BlockMatchers::OptionalBlock.new @ordering_constraints = [] @side_effects = [] - @cardinality = Cardinality.new.exactly(1) + @cardinality = Cardinality.new.range(1, 1) @return_values = ReturnValues.new @yield_parameters = YieldParameters.new @backtrace = backtrace || caller diff --git a/test/unit/cardinality_test.rb b/test/unit/cardinality_test.rb index 1c265e97a..eda77a42b 100644 --- a/test/unit/cardinality_test.rb +++ b/test/unit/cardinality_test.rb @@ -34,39 +34,39 @@ def test_should_be_satisfied_if_invocations_so_far_have_reached_required_thresho end def test_should_describe_cardinality_defined_using_at_least - assert_equal 'allowed any number of times', Cardinality.new.at_least(0).anticipated_times - assert_equal 'expected at least once', Cardinality.new.at_least(1).anticipated_times - assert_equal 'expected at least twice', Cardinality.new.at_least(2).anticipated_times - assert_equal 'expected at least 3 times', Cardinality.new.at_least(3).anticipated_times + assert_equal 'allowed any number of times', Cardinality.new.range(0).anticipated_times + assert_equal 'expected at least once', Cardinality.new.range(1).anticipated_times + assert_equal 'expected at least twice', Cardinality.new.range(2).anticipated_times + assert_equal 'expected at least 3 times', Cardinality.new.range(3).anticipated_times end def test_should_describe_cardinality_defined_using_at_most - assert_equal 'expected at most once', Cardinality.new.at_most(1).anticipated_times - assert_equal 'expected at most twice', Cardinality.new.at_most(2).anticipated_times - assert_equal 'expected at most 3 times', Cardinality.new.at_most(3).anticipated_times + assert_equal 'expected at most once', Cardinality.new.range(0, 1).anticipated_times + assert_equal 'expected at most twice', Cardinality.new.range(0, 2).anticipated_times + assert_equal 'expected at most 3 times', Cardinality.new.range(0, 3).anticipated_times end def test_should_describe_cardinality_defined_using_exactly - assert_equal 'expected never', Cardinality.new.exactly(0).anticipated_times - assert_equal 'expected exactly once', Cardinality.new.exactly(1).anticipated_times - assert_equal 'expected exactly twice', Cardinality.new.exactly(2).anticipated_times - assert_equal 'expected exactly 3 times', Cardinality.new.exactly(3).anticipated_times + assert_equal 'expected never', Cardinality.new.range(0, 0).anticipated_times + assert_equal 'expected exactly once', Cardinality.new.range(1, 1).anticipated_times + assert_equal 'expected exactly twice', Cardinality.new.range(2, 2).anticipated_times + assert_equal 'expected exactly 3 times', Cardinality.new.range(3, 3).anticipated_times end def test_should_describe_cardinality_defined_using_times_with_range - assert_equal 'expected between 2 and 4 times', Cardinality.new.times(2..4).anticipated_times - assert_equal 'expected between 1 and 3 times', Cardinality.new.times(1..3).anticipated_times + assert_equal 'expected between 2 and 4 times', Cardinality.new.range(2, 4).anticipated_times + assert_equal 'expected between 1 and 3 times', Cardinality.new.range(1, 3).anticipated_times end def test_should_need_verifying - assert Cardinality.new.exactly(2).needs_verifying? - assert Cardinality.new.at_least(3).needs_verifying? - assert Cardinality.new.at_most(2).needs_verifying? - assert Cardinality.new.times(4).needs_verifying? - assert Cardinality.new.times(2..4).needs_verifying? + assert Cardinality.new.range(2, 2).needs_verifying? + assert Cardinality.new.range(3).needs_verifying? + assert Cardinality.new.range(0, 2).needs_verifying? + assert Cardinality.new.range(4).needs_verifying? + assert Cardinality.new.range(2, 4).needs_verifying? end def test_should_not_need_verifying - assert_equal false, Cardinality.new.at_least(0).needs_verifying? + assert_equal false, Cardinality.new.range(0).needs_verifying? end end