On-the-fly Mock classes
Some time ago, I implemented a neat hack in SUnit that I had a use for again today. I thought I might share it here.
The issue is that you often want to have a subclass of a tested class that overrides one or two methods with very specific (to that test) behavior. Say you have a user class, and somewhere in your code the messsage #save is sent. During your test, you just want to know whether the message is sent. This hack allows you to do so right in the test code.
Without further ado, the methods in TestCase I added:
TestCase>>makeMockSubclassOf: class
instanceVariables: ivars
methods: methods
classMethods: classMethods
| answer |
answer := ClassBuilder new
newSubclassOf: class
type: #normal
instanceVariables: ivars
from: nil.
methods do: [:each |
answer compileSilently: each classified: nil].
classMethods do: [:each |
answer class compileSilently: each classified: nil].
^answer
TestCase>>withMockSubclassOf: class
instanceVariables: ivars
methods: methods
classMethods: classMethods
do: aBlock
| mock |
mock := self makeMockSubclassOf: class
instanceVariables: ivars
methods: methods
classMethods: classMethods.
[aBlock value: mock]
ensure: [class removeSubclass: mock]
The second method is the one you’ll use. For example:
MyTestCase>>testUserSave
self withMockSubclassOf: User
instanceVariables: 'isSaved'
methods: #(
'isSaved ^isSaved'
'save isSaved := true')
classMethods: #()
do: [:mock |
| user |
user := mock new.
user doSomethingThatWillCallSave.
self assert: user isSaved]
At the end of the test, the mock class will be removed.
The advantage to this method over the “classical” method of creating stand-alone mock subclasses, is that you can really tailor the behavior to your test (mock classes tend to grow to accumulate a number of tests), and that the mock behaviour is “right there”.



December 23rd, 2005 at 2:47 pm
I end up do a fair bit of this with anonymous inner classes in Java and was wonder how do this in Smalltalk.
Thanks!
Wilkes