Skip to: Site menu | Main content

Groovy 

      Download | Documentation | Developers | Community

An agile dynamic language for the Java Platform

Developer Testing using Maps and Expandos instead of Mocks Add comment to Wiki View in Wiki Edit Wiki page Printable Version

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
    }
}

See also: Developer Testing using Closures instead of Mocks