Anyway.
Innocently trying to run some unit tests on an existing set of Grails controllers. Everybody else seems to be running them fine, but I get:
| Running 9 unit tests... 1 of 9 | Failure: testThatAAABBDDD(BrokenAssTests) | java.lang.NullPointerException: Cannot set property 'method' on null object at BrokenAssTests.setup(BrokenAssTests.groovy:155) | Running 9 unit tests... 2 of 9 | Failure: testThatEEEFFGGG(BrokenAssTests) | java.lang.NullPointerException: Cannot set property 'method' on null object at BrokenAssTests.setup(BrokenAssTests.groovy:155)
The setup() method in question:
@Before public void setup() { request.method = 'GET' }
Same Groovy version, same Grails version, very similar Java version (1.7.0_11 vs 1.7.0_21, hardly a biggie), so what's going on?
I'm running Ubuntu 12.04, everyone else is on Macs (a story for another day).
A great deal of digging and Googling later, and here's what's going on:
- Grails 2.0 introduced the TestFor annotation which automagically mixes in the ControllerUnitTestMixin
- As one of its many magic tricks, the ControllerUnitTestMixin adds mocked request and response objects into the test scope
- This magic is performed in method bindGrailsWebRequest() which if you persue the documentation, has the JUnit 4 @Before annotation applied
- The only ordering guarantee about @Before evaluation is "superclass comes before subclass"
- But a mixin is NOT a superclass!
- And so the order of @Before evaluation when using Grails' test mixins is essentially OS/JVM dependent
@Before public void setup() { if (!request) { bindGrailsWebRequest() // Make sure request is visible - the ordering of @Befores is not guaranteed :-( } request.method = 'GET' }I don't like it much, and hopefully the next version of Grails rectifies this, but for me it's just another one of those "too many layers of magic" problems that in my experience seems to afflict the Groovy world more than others.