Skip to content

Commit

Permalink
Merge pull request #26 from dionisiydk/expectationWithoutStub
Browse files Browse the repository at this point in the history
Prevent user mistakes with missing #stub message
  • Loading branch information
dionisiydk authored Feb 20, 2020
2 parents 076d19d + 7817afa commit fdeea79
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 7 deletions.
13 changes: 13 additions & 0 deletions Mocketry-Domain-Tests/MockAcceptanceTests.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,19 @@ MockAcceptanceTests >> testBuildingExpectationForMocksReturnedFromMessages [
mock someMessage2 extraMessage should be: #result
]

{ #category : #tests }
MockAcceptanceTests >> testBuildingExpectationWithoutStubShouldAlwaysFail [

[mock someMessage willReturn: #result]
should fail.

[mock willReturn: #result]
should fail.

[mock stub willReturn: #result]
should fail.
]

{ #category : #tests }
MockAcceptanceTests >> testBuildingExpectationsByBlock [

Expand Down
5 changes: 4 additions & 1 deletion Mocketry-Domain/MockBehaviour.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,10 @@ MockBehaviour >> send: aMessage to: aMockObject [
receiver: aMockObject
selector: aMessage selector
arguments: aMessage arguments.

"Following is a hook to prevent user mistakes do to missing #stub message for expectations.
Check method comment for details"
occurredMessage shouldBeAllowedForMock.

^mockRole processMessageSend: occurredMessage by: self
]

Expand Down
32 changes: 26 additions & 6 deletions Mocketry-Domain/MockExpectedMessage.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,17 @@ And to specify conditions which should be valid when expectation is executed use
- when: aBlock is: aSpecOfObjectState
- when: aBlock satisfying: aBlock
- shouldOccurInThisThread
- shouldOccurInAnotherThread
- shouldBeSentInThisThread
- shouldBeSentInAnotherThread
In addition I mark particular methods with pragma <dontSendToMock>. It allows to prevent user mistakes when expectations are defined without #stub message to the mock. It is easy to do such mistake. So such messages (mock teacher API) are completely forbidden for mocks. For example following expressions fail:
mock someMessage willReturn: 1.
mock will: [2].
mock stub willRaise: Error new
It means that users can't stub such messages but it is reasonable restriction.
Internal Representation and Key Implementation Points.
Instance Variables
Expand All @@ -49,7 +57,7 @@ Class {
'actions',
'conditionsSpec'
],
#category : 'Mocketry-Domain'
#category : #'Mocketry-Domain'
}

{ #category : #'instance creation' }
Expand Down Expand Up @@ -125,11 +133,13 @@ MockExpectedMessage >> restrictUsage [

{ #category : #conditions }
MockExpectedMessage >> shouldBeSentInAnotherProcess [
<dontSendToMock>
conditionsSpec addSpec: SpecOfAsynchMessage forActiveProcess
]

{ #category : #conditions }
MockExpectedMessage >> shouldBeSentInThisProcess [
<dontSendToMock>
conditionsSpec addSpec: SpecOfAsynchMessage forActiveProcess not
]

Expand Down Expand Up @@ -178,52 +188,62 @@ MockExpectedMessage >> useTwice [
]

{ #category : #conditions }
MockExpectedMessage >> when: subjectBlock is: aSpecOfOBjectState [
conditionsSpec addSpec: (SpecOfMessageSendCondition of: subjectBlock by: aSpecOfOBjectState)
MockExpectedMessage >> when: subjectBlock is: aSpecOfObjectState [
<dontSendToMock>
conditionsSpec addSpec: (SpecOfMessageSendCondition of: subjectBlock by: aSpecOfObjectState)
]

{ #category : #conditions }
MockExpectedMessage >> when: subjectBlock satisfy: conditionBlock [
<dontSendToMock>
self when: subjectBlock is: (Satisfying for: conditionBlock)
]

{ #category : #actions }
MockExpectedMessage >> will: anObject [
<dontSendToMock>
actions add: anObject asMockExpectationAction
]

{ #category : #actions }
MockExpectedMessage >> willCallOriginalMethod [
<dontSendToMock>
self will: MockExpectedOriginalMethodCall new
]

{ #category : #actions }
MockExpectedMessage >> willLogMessage [
<dontSendToMock>
self will: MockExpectedMessageLogging new
]

{ #category : #actions }
MockExpectedMessage >> willRaise: anExceptionOrClass [
<dontSendToMock>
self will: (MockExpectedException exception: anExceptionOrClass)
]

{ #category : #actions }
MockExpectedMessage >> willReturn: anObject [
<dontSendToMock>
self will: (MockExpectedValueReturn value: anObject)
]

{ #category : #actions }
MockExpectedMessage >> willReturnValueFrom: anArray [
<dontSendToMock>
self will: (MockExpectedValueForForEachCall values: anArray).
spec usage maxCount: anArray size
]

{ #category : #actions }
MockExpectedMessage >> willReturnYourself [
<dontSendToMock>
self will: MockExpectedReceiverReturn new
]

{ #category : #actions }
MockExpectedMessage >> willStubRealResult [
<dontSendToMock>
self will: MockExpectedMethodResultStub new
]
19 changes: 19 additions & 0 deletions Mocketry-Domain/MockOccurredMessage.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,25 @@ MockOccurredMessage >> setUpUnexpectedResult [
^self extractResultFrom: [ receiver stubDoesNotExpect: self ]
]

{ #category : #controlling }
MockOccurredMessage >> shouldBeAllowedForMock [
"Method is introduced to prevent user mistakes due to missing #stub message.
It is easy to forget to send #stub message when defining an expectation for a mock.
So with this method following examples fail with explicit error:
mock someMessage willReturn: 3.
mock will: [3].
mock stub willRaise: Error new"
| expectationMethod |
expectationMethod := MockExpectedMessage compiledMethodAt: selector ifAbsent: [ ^self ].
(expectationMethod hasPragmaNamed: #dontSendToMock) ifFalse: [ ^self ].

GHCurrentMetaLevelDepth increaseFor: [ | error |
error := receiver ghostBehaviour mockRole isTeaching
ifTrue: [ 'Missing expected message (after #stub)' ] ifFalse: [ 'Missing #stub' ].
self error: error, ' to define expectation using ', selector printString
]
]

{ #category : #printing }
MockOccurredMessage >> stringForResultSpec [

Expand Down
5 changes: 5 additions & 0 deletions Mocketry-Domain/MockRole.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ MockRole class >> default [
^default ifNil: [ default := self new ]
]

{ #category : #testing }
MockRole >> isTeaching [
^false
]

{ #category : #processing }
MockRole >> processMessageSend: anOccurredMessage by: aMockBehaviour [
self subclassResponsibility
Expand Down
5 changes: 5 additions & 0 deletions Mocketry-Domain/MockStubTeacher.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ MockStubTeacher >> anyMessage [
Any stub anyMessage willReturn: 10 "
]

{ #category : #testing }
MockStubTeacher >> isTeaching [
^true
]

{ #category : #processing }
MockStubTeacher >> processTransformedMessageSend: anOccurredMessage by: aMockBehaviour [

Expand Down
5 changes: 5 additions & 0 deletions Mocketry-Domain/MockTeacher.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ Class {
#category : 'Mocketry-Domain'
}

{ #category : #testing }
MockTeacher >> isTeaching [
^true
]

{ #category : #processing }
MockTeacher >> processMessageSend: anOccurredMessage by: aMockBehaviour [

Expand Down

0 comments on commit fdeea79

Please sign in to comment.