So, I just threw out most of this morning trying to figure out why something which clearly should work was blowing up my unit test on a grails app. To spare you the same pain I’m documenting it here.
The scenario is that I have Roles and Privileges as domain classes. A role has many privileges, and any privilege may belong to one or more roles. This is represented in domain classes pretty succinctly as;
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
So I can access all the privileges which belong to a role pretty easily, but what if I want to know all roles which a particular privilege belongs to? Easy enough, we can look that up in a variety of ways. Below I show adding a new closure to the privilege domain class which uses the withCriteria functionality of GORM to return all roles which have this privilege in the privileges map. The new closure is in the highlighted lines.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Now, if you’re doing proper test driven development (you are doing TDD, right?!), you’d probably already have a test written for this new closure that would look something like the highlighted lines of the test fixture below. Notice lines 8 and 9 which are also highlighted to show that we’re asking the framework to mock out the GORM methods on the role and privilege classes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
Even after you’ve implemented getRolesWithThisPrivilege on the Privileges domain class though, you’ll find your test still fails with an error that looks like the following.
No signature of method: com.nslms.mockdomainlimitations.Role.withCriteria() is applicable for argument types: (com.nslms.mockdomainlimitations.Privilege$_closure1_closure3) values: [com.nslms.mockdomainlimitations.Privilege$_closure1_closure3@8327473]
In short, it’s telling us that the withCriteria method of GORM isn’t implemented in the context of our test. Of course if you put the exact same code in an integration test you’re golden.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
With this in place, you can run a “grails test-app -integration” and the exact same test which failed during unit testing will succeed. This is of course because the entire grails bootstrapping occurs, and all of the artifacts (like domain classes) are wired up fully by the framework.
So the moral of the story? If you’re planning to test anything more than simple saves with GORM in your testing phase, consider putting the more complex stuff into an integration test. Either that, or keep your eyes peeled for problems like this and be prepared to refactor.
Feel free to grab a copy of the test grails app I created for this example.
* UPDATE: This example app has a new home..
Grab the project