Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implemented verify[Zero|NoMore]Interactions

  • Loading branch information...
commit b5313a807228444b530cb5784b7900ebd1dae303 1 parent a94d464
@dhasenan authored
Showing with 91 additions and 18 deletions.
  1. +12 −2 README.md
  2. +65 −15 lib/maryjane.coffee
  3. +14 −1 test/maryjanetests.coffee
View
14 README.md
@@ -103,11 +103,22 @@ You can also verify the number of times something should be called:
verify(well, atMost(3)).consume(apple);
verify(well, between(3, 7)).consume(apple);
-Currently, you can't check that a mock had no interactions or no unverified interactions. When this is available, it will appear as:
+If you want to check that you've covered all the cases, or that a particular mock hasn't been used in this case:
verifyNoMoreInteractions(well1, well2);
verifyZeroInteractions(well3, well4);
+Both of these ignore anything you did in the Arrange phase. For example, the following test will pass:
+
+ when(portalGun).firePortal('up').thenReturn('You made a hole in the ceiling');
+ verifyZeroInteractions(portalGun);
+
+This is useful when you have an excessively complicated test, or when you use a common setup method that sets up some return values but this particular method does not make use of them.
+
+Both of these can take multiple mocks:
+
+ verifyZeroInteractions(turret1, turret2, weightedStorageCube);
+
Using Callbacks
---------------
When you supply a callback on a method with `thenDo`, the method you supply stands in for the called method.
@@ -121,6 +132,5 @@ This will replace the `add` function on `math` with one that returns the bitwise
TODO
====
- * verifyNoMoreInteractions, verifyZeroInteractions
* Ordering
* Argument matchers
View
80 lib/maryjane.coffee
@@ -52,8 +52,13 @@ class Range
# the interactions should be all-or-nothing.
#
# It's a pretty stupid use case, but we support it!
-exports.verifyZeroInteractions = (mock) ->
- onMock mock, (m) -> m._mockInternals.verifyZeroInteractions()
+exports.verifyZeroInteractions = (mocks...) ->
+ for mock in mocks
+ onMock mock, (m) -> m._mockInternals.verifyZeroInteractions()
+
+exports.verifyNoMoreInteractions = (mocks...) ->
+ for mock in mocks
+ onMock mock, (m) -> m._mockInternals.verifyNoMoreInteractions()
# Repeat functions
exports.times = (min, max) -> new Range(min, max)
@@ -98,8 +103,8 @@ class MockInternals
for key, value of @type
addFieldOrMethod(@mock, @type, key)
- @expectedMethodCalls = []
- @unexpectedMethodCalls = []
+ @preparedMethodCalls = []
+ @unpreparedMethodCalls = []
@recording = false
@checking = false
@typeName = getName(@type)
@@ -111,36 +116,39 @@ class MockInternals
if @checking
return @check field, args
- m = @findCall @expectedMethodCalls, field, args
+ m = @findCall @preparedMethodCalls, field, args
if m?
return m.execute args
- m = @findCall @unexpectedMethodCalls, field, args
+ m = @findCall @unpreparedMethodCalls, field, args
if !m?
m = new MockOptions(@mock, field, args)
- @unexpectedMethodCalls.push m
+ @unpreparedMethodCalls.push m
m.alreadyRan()
null
check: (field, args) ->
@checking = false
- m = @findCall @expectedMethodCalls, field, args
+ m = @findCall @preparedMethodCalls, field, args
if m?
if ! (m instanceof MockOptions)
throw new Error 'malformed recorded expectation'
if !@range.match m.count()
@failCheck field, args, m
+ m.checkedRange @range
else
- m = @findCall @unexpectedMethodCalls, field, args
+ m = @findCall @unpreparedMethodCalls, field, args
count = if !m? then 0 else m.count()
if !@range.match count
@failCheck field, args, m
+ if m?
+ m.checkedRange @range
null
record: (field, args) ->
@recording = false
m = new MockOptions(@mock, field, args)
- @expectedMethodCalls.push m
+ @preparedMethodCalls.push m
m
findCall: (list, field, args) ->
@@ -163,19 +171,45 @@ class MockInternals
@range = times
@mock
+ verifyNoMoreInteractions: ->
+ # First let's run through and see if we have any excessive calls
+ excessive = []
+ for call in @unpreparedMethodCalls
+ if call.verified() and call.inExpectedRange()
+ continue
+ excessive.push call
+ for call in @preparedMethodCalls
+ if call.verified() and call.inExpectedRange()
+ continue
+ excessive.push call
+
+ if excessive.length == 0
+ return
+
+ expected = 'Expected:'
+ actual = 'Actual:'
+ for call in excessive
+ expected += '\n\t'
+ expected += call.expectationDescription()
+ actual += '\n\t'
+ actual += call.callDescription()
+
+ throw new Error expected + '\n' + actual
+
+
verifyZeroInteractions: ->
- if @unexpectedMethodCalls.length == 0 && @expectedMethodCalls.length == 0
+ if @unpreparedMethodCalls.length == 0 && @preparedMethodCalls.length == 0
return
first = true
result = 'Expected no interactions with ' + @typeName + ', but '
found = false
- for a in @unexpectedMethodCalls
+ for a in @unpreparedMethodCalls
found = true
if !first
result += '\n\t'
result += a.callDescription()
first = false
- for a in @expectedMethodCalls
+ for a in @preparedMethodCalls
if a.count() > 0
found = true
if !first
@@ -268,5 +302,21 @@ class MockOptions
count: ->
@_count
- callDescription: ->
- return formatMethodCall(@_mock._mockInternals.typeName, @_name, @_args) + ' was called ' + @_count + ' times'
+ verified: ->
+ @_verified
+
+ checkedRange: (range) ->
+ @_checkedRange = range
+
+ inExpectedRange: ->
+ @_checkedRange.match @_count
+
+ expectationDescription: ->
+ if @_checkedRange?
+ return @method() + ' should be called ' + @_checkedRange.toString()
+ else
+ return @method() + ' should not be called'
+
+ method: -> formatMethodCall(@_mock._mockInternals.typeName, @_name, @_args)
+
+ callDescription: -> @method() + ' was called ' + @_count + ' times'
View
15 test/maryjanetests.coffee
@@ -26,7 +26,6 @@ exports['leave the original type alone'] = ->
exports['return null by default'] = ->
b = mj.mock(new UnderTest())
- assert.eql b._mockInternals.expectedMethodCalls.length, 0
assert.isNull b.frob(1, 7)
exports['wrong arguments ignored'] = ->
@@ -259,6 +258,20 @@ exports['number of times called, range, too low'] = ->
assert.throws cb, (ex) ->
ex.message == 'Expected UnderTest.frob(1, 7) to be called between 1 and 3 times, but it was called 4 times'
+exports['verifyNoMoreInteractions'] = ->
+ mock = mj.mock(new UnderTest())
+ mj.verifyNoMoreInteractions(mock)
+
+exports['verifyNoMoreInteractions failure'] = ->
+ mock = mj.mock(new UnderTest())
+ mock.frob(1, 7)
+ mock.frob(1, 7)
+ mock.frob(1, 7)
+ mock.frob(1, 7)
+ cb = -> mj.verifyNoMoreInteractions(mock)
+ assert.throws cb, (ex) ->
+ ex.message == 'Expected:\n\tUnderTest.frob(1, 7) should not be called\nActual:\n\tUnderTest.frob(1, 7) was called 4 times'
+
exports['verifyZeroInteractions'] = ->
mock = mj.mock(new UnderTest())
mj.verifyZeroInteractions(mock)
Please sign in to comment.
Something went wrong with that request. Please try again.