Suppose we are trying to test the following application:
class MyApp {
def factory
def logger
def doBusinessLogic(param) {
def myObj = factory.instance
myObj.doSomething(param)
myObj.doSomethingElse(param)
logger.log('Something done with: ' + param)
}
}
We might be tempted to replace logger and factory with Groovy's built-in mocks, but it turns out that Maps or Expandos are often sufficiently powerful enough in Groovy that we don't need full blown dynamic mocks for this example.
Instead of a mock for logger, we can use an Expando as follows:
...
def logger = new Expando()
logger.log = { msg -> assert msg == 'Something done with: ' + param }
...
Here the expected behaviour for our production code is captured within a Closure. (When using TDD, this closure would force the production code we saw in our original code to be created.)
Instead of a mock for factory, we can use a simple map as follows:
...
def factory = [instance: businessObj]
...
Here, businessObj is the object we want the factory to return, though in general this could be a Closure similar to what we did for the Expando above.
Putting this altogether yields (after some refactoring) the following complete test:
class MyAppTest extends GroovyTestCase { void testDoesBusinessLogic() { // triangulate checkDoesBusinessLogic "case1" checkDoesBusinessLogic "case2" } private checkDoesBusinessLogic(param) { def logger = setUpLoggingExpectations(param) def businessObj = setUpBusinessObjectExpectations(param) def factory = [instance: businessObj] def cut = new MyApp(logger:logger, factory:factory) cut.doBusinessLogic(param) } private setUpLoggingExpectations(param) { def shouldProduceCorrectLogMessage = { msg -> assert msg == 'Something done with: ' + param } def logger = new Expando() logger.log = shouldProduceCorrectLogMessage return logger } private setUpBusinessObjectExpectations(param) { def shouldBeCalledWithInputParam = { assert it == param } def myObj = new Expando() myObj.doSomething = shouldBeCalledWithInputParam myObj.doSomethingElse = shouldBeCalledWithInputParam return myObj } }






