Grails Domain Mocking Limitations

- - posted in Grails/Groovy | Comments

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;

/grails-app/domain/com/nslms/mockdomainlimtations/Role.groovy
1
2
3
4
5
6
7
8
9
package com.nslms.mockdomainlimitations

class Role {
  static hasMany = [privileges: Privilege]

  static constraints = {
  }
  String name
}
/grails-app/domain/com/nslms/mockdomainlimtations/Privilege.groovy
1
2
3
4
5
6
7
8
9
package com.nslms.mockdomainlimitations

class Privilege {

  static constraints = {
  }

  String name
}

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.

/grails-app/domain/com/nslms/mockdomainlimtations/Privilege.groovy (with new closure) mark:10,11,12,13,14,15,16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.nslms.mockdomainlimitations

class Privilege {

  static constraints = {
  }

  String name

  def getRolesWithThisPrivilege = {
    Role.withCriteria() {
      privileges {
        eq('id', this.id)
      }
    }
  }
}

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.

/test/unit/com/nslms/mockdomainlimitaions/PrivilegeTests.groovy mark:8,9,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32
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
package com.nslms.mockdomainlimitations

import grails.test.*

class PrivilegeTests extends GrailsUnitTestCase {
  protected void setUp() {
    super.setUp()
    MockUtils.mockDomain(Role.class)
    MockUtils.mockDomain(Privilege.class)
  }

  protected void tearDown() {
    super.tearDown()
  }

  void testAbilityToGetAListOfRolesAPrivilegeBelongsTo() {
    def role1 = new Role(name: 'Administrator')
    def role2 = new Role(name: 'User')

    def priv1 = new Privilege(name: 'ReadAll').save(flush: true)

    role1.addToPrivileges(priv1)
    role2.addToPrivileges(priv1)

    role1.save(flush: true)
    role2.save(flush: true)

    def roleList = priv1.getRolesWithThisPrivilege()

    assert roleList.size() == 2
    assert roleList == [role1, role2]
  }
}

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.

/test/integration/com/nslms/mockdomainlimitations/PrivilegeTest.groovy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.nslms.mockdomainlimitations

class PrivilegeTest extends GroovyTestCase {
  void testAbilityToGetAListOfRolesAPrivilegeBelongsTo() {
    def role1 = new Role(name: 'Administrator')
    def role2 = new Role(name: 'User')

    def priv1 = new Privilege(name: 'ReadAll').save(flush: true)

    role1.addToPrivileges(priv1)
    role2.addToPrivileges(priv1)

    role1.save(flush: true)
    role2.save(flush: true)

    def roleList = priv1.getRolesWithThisPrivilege()

    assert roleList.size() == 2
    assert roleList == [role1, role2]
  }
}

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.

svn export https://linode.nslms.com/svn_ro/MockDomainLimitations

* UPDATE: This example app has a new home..

Grab the project

1
git clone git://ec2.nslms.com/grails/blog_example_mock_limitations

Comments