Grails Unit Testing and a little fun with @Before

So, we’ve been using Grails at gibbon’s five for a while now and stumbled into different problems with our unit tests. One of the weirdest ones were occasionally failing tests on our Jenkins that never failed locally. So we digged into this a little and came up with a simple explanation, that I’d like to share with you.

Let’s assume we have a controller and want to test it. First thing you’d probably do is go to the Grails Guide, Chapter 10: Unit Testing Controllers and start with something like this:

import grails.test.mixin.TestFor

@TestFor(SimpleController)
class SimpleControllerTests {
    void testSomething() {
    }
}

Now, if you have multiple tests which need some variable set inside the (controller.)request and you have a little knowledge of JUnit, you’d probably come up with something like this:

import grails.test.mixin.TestFor
import org.junit.Before

@TestFor(SimpleController)
class SimpleControllerTests {
    @Before
    void initialize() {
        request.variable = "foobar"
    }
    void testSomething() {
        // some code here needs the request.variable
    }
    void testSomethingElse() {
        // some code here needs the request.variable
    }
}

This is bad.


Explanation: Grails is doing it’s magic when starting this test. In this case, it let’s your SimpleControllerTests extend grails.test.mixin.web.ControllerUnitTestMixin which is not abstract and also has a method annotated with @Beforehere‘s the whole list of mixins:

@Before
void bindGrailsWebRequest() {
    mockCodec(Base64Codec)
    ...
    if (webRequest == null) {
        webRequest = GrailsWebRequest.lookup()
        if (webRequest == null || !(webRequest.currentRequest instanceof GrailsMockHttpServletRequest)) {
            if (!applicationContext.isActive()) {
                applicationContext.refresh()
            }
            ...
            request = webRequest.getCurrentRequest()
            response = webRequest.getCurrentResponse()
            servletContext = webRequest.getServletContext()
        }
        ...
    }
}

Well, now we have two methods annotated with @Before and let’s put it this way: it’s in god’s hands to decide which one is executed first [1] :poke:

So what can you do to avoid this?
We’re currently using two different solutions:

1. Manually executing super.bindGrailsWebRequest()
This is our quick fix for old tests that occasionally fail. We simply let them extend their Mixin manually and execute the super method manually:

import grails.test.mixin.TestFor
import grails.test.mixin.web.ControllerUnitTestMixin
import org.junit.Before

@TestFor(SimpleController)
class SimpleControllerTests extends ControllerUnitTestMixin {
    @Before
    void initialize() {
        super.bindGrailsWebRequest()
        request.variable = "foobar"
    }
    void testSomething() {
        // some code here needs the request.variable
    }
    void testSomethingElse() {
        // some code here needs the request.variable
    }
}

While this seems pretty straight forward and easy to do, it comes with a tradeoff: If the grails code is adapted or changed, this might break these tests. Also it kills some of the magic grails is doing, which is one of the reasons it’s a nice framework.

2. Using a framework/plugin that is offering something to solve this: Spock
The other solution we’re working with is to rewrite the tests using Spock. Spock offers so called fixture methods (see SpockBasics) which can be used to replace the @Before methods. On top it offers a whole bunch of new possibilities to write nicer tests.

Spock is a testing and specification framework for Java and Groovy applications. What makes it stand out from the crowd is its beautiful and highly expressive specification language. Thanks to its JUnit runner, Spock is compatible with most IDEs, build tools, and continuous integration servers. Spock is inspired from JUnit, RSpec, jMock, Mockito, Groovy, Scala, Vulcans, and other fascinating life forms.

I won’t go into much detail but here’s how the code would look like in the example:

import grails.test.mixin.TestFor

@TestFor(SimpleController)
class SimpleControllerSpec extends spock.lang.Specification {
    def setup() {
        request.variable = "foobar"
    }
    def testSomething() {
        // some code here needs the request.variable
    }
    def testSomethingElse() {
        // some code here needs the request.variable
    }
}

This is our prefered solution but there’s also a tradeoff: You have to rewrite a lot of your tests. On the other hand this might be a good idea, because revisiting and refactoring old tests that you didn’t look into for a while is always a good thing ;)

I hope this might help some other people to avoid the obstacles we crashed into :)

Leave a Reply