Posts Tagged ‘GroovyTestCase’

Using Groovy’s built in unit test facilities

May 18, 2009

Automated testing increases code quality. Though your compiler may already detect some coding errors, you can detect most errors only at runtime. In a dynamic language as Groovy, even less errors are detected by your compiler, so automated testing becomes even more important.

For Java-testing I use JUnit. In Groovy, you can use some very useful Groovy classes for testing, that extend JUnit functionality with extra features. Groovy testing is based on JUnit 3. Though you can also use JUnit 4 with Groovy, it’s probably easier to use Groovy’s built in unit testing facilities.

GroovyTestCase

Let’s first have a look at Groovy’s version of the TestCase class. It is called GroovyTestCase and extends the TestCase class already provided in JUnit. Apart from the already available assert methods like assertEquals and assertTrue, it adds a number of useful assertions, like assertArrayEquals, assertLength and assertContains. Also, a shouldFail method is added, for checking exceptions:

class MyTestCase extends GroovyTestCase {
    void testException() {
        def num = "12.3{"
        def val
        shouldFail(NumberFormatException) {
            val = num as Long
        }
        assert val == null
    }
}

The code that should fail is passed to the shouldFail method as a closure. Easy, eh? Also note the use of the assert keyword instead of the assertTrue method in line 8. This is not uncommon in Groovy. Apart from running the test from a test suite, you can even run the test from the command line. Just type:

groovy MyTestCase

Since MyTestCase extends GroovyTestCase, groovy will know what to do.

Note: JUnit 3 requires a test method to be called testXXX() and to be of type void. Therefore, we have to explicitly define the type of the test method as void. The following code is equivalent to defining a method with return type Object (returning null), and is NOT detected as a test

def testException() {
    // wrong! return type of method is compiled to Object, making it not a test method!
}

StubFor

Very often, classes cooperate. Of course you could write code for testing a number of classes at the same time. Integration testing can be an important part of your test strategy. However, before you do integration tests, you should unit test your classes in isolation. If your class under test depends on other classes, those other classes should be stubbed or mocked. Stubs or mocks provide predefined, fake functionality that is used specifically in your test.

The following code defines a stub:

import groovy.mock.interceptor.*

class Collaborator {}

def stubber = new StubFor(Collaborator.class)
stubber.demand.one()  { "calling one" }
stubber.demand.two()  { arg -> "two(${arg}), first time" }
stubber.demand.two()  { arg -> "two(${arg}), second time" }
stubber.use {
    def stub = new Collaborator()
    assert "two(hi), first time" == stub.two("hi")
    assert "two(there), second time" == stub.two("there")
}

Within the use clause, each instantiation of the stubbed class returns a stub for the class, instead of a real class. Not that, although we defined a “one” method for the stub, we never called it.

MockFor

A stub provides functionality for your test, but does not check if it is called correctly. In the above example, we might want to check that the test actually calls the defined methods, and calls them in the right order. In that case, we use a mock instead of a stub. We can easily demonstrate this by replacing StubFor in line 5 of the above example by MockFor, and run the example.

Exception thrown: No call to 'two' expected at this point. Still 1 call(s) to 'one' expected.

Groovy now checks that you call the one method first, before you are calling the two method twice. You can also specify that a
function must be called a number of times:

stubber.demand.one(1..3)  { "calling one" }

The function must now be called one to three times, returning “calling one” each time. If you do not call it the required number of times, you’ll get an assertion error.

Explicitly proxying a stub

With the use keyword, the new Collaborator() constructor is diverted to a stub or mock constructor. You can also call the mock or stub constructor explicitly, without a use keyword. If you do so, for a mock you must explicitly verify the method calls, by calling the verify() method. For a stub, you can call the verify() method. The difference between mocks and stubs, is that for mocks, both the number of method calls and the order of the method calls is verified. This is the same behaviour as when you use the use keyword. For stubs, only the number of method calls will be verified. This is not the same behaviour as when you use the use keyword. If you use the use keyword on a stub, by default the verify method is not called.

In the next example, we explicitly call the stub constructor. At the end, we add a call to the verify method, so that the number of method calls is checked. This condition won’t be checked when using the use keyword. The example will run perfectly, even if the order of the method calls differs. The number of method calls is correct, and that is what is verified.

def stubber = new StubFor(Collaborator.class)
stubber.demand.one()  { "calling one" }
stubber.demand.two()  {  "calling two" }
def stub = new stubber.proxyInstance()
assert "calling two" == stub.two()
assert "calling one" == stub.one()
stubber.verify(stub)
Advertisements