Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I correctly move grails unit-test helper methods into a seperate file?

I have some often-used helper methods for unit tests put into a seperate file. The idea is to, for example, allow my XYZTests.groovy to call TestHelper.getUserObject() in order to get a fully initialized instance of User.

Now the problem is, that there's a springSecurityService.encodePassword(pw) being called in the User's beforeInsert() which always fails as there's no mock for springSecurityService in TestHelper.groovy.

java.lang.NullPointerException: Cannot invoke method encodePassword() on null object

In User.groovy:

def beforeInsert() {
    // ...
    password = springSecurityService.encodePassword(pw)
    // ...
}

Note: I would like to avoid any mocking in TestHelper.groovy in order to use it's methods in integration tests too.

In spite of that, even if I try to call a mockFor() anywhere in the TestHelper.groovy, I get an MME:

No signature of method: static myproject.TestHelper.mockFor() is applicable for argument types: (java.lang.Class, java.lang.Boolean) values: [class grails.plugins.springsecurity.SpringSecurityService, true]
groovy.lang.MissingMethodException: No signature of method: static myproject.TestHelper.mockFor() is applicable for argument types: (java.lang.Class, java.lang.Boolean) values: [class grails.plugins.springsecurity.SpringSecurityService, true]
    at myproject.TestHelper.mockSpringSecurityService(TestHelper.groovy:59)
    at myproject.TestHelper$mockSpringSecurityService.callStatic(Unknown Source)
    at myproject.TestHelper.getUserObject(TestHelper.groovy:47)
    at myproject.TestHelper$getUserObject.call(Unknown Source)
    at myproject.UserTests.setUp(UserTests.groovy:26)

Note: I currently mock the springSecurityService.encodePassword like this:

// in UserTests.groovy
protected void setUp() {
    // mockDomain(...) and such here

    def u = TestHelper.getUserObject("Pummel")
    u.springSecurityService = mockSpringSecurityService()
    assert u.save()
}
private mockSpringSecurityService() {
    def ssService = mockFor(SpringSecurityService,true)
    ssService.metaClass.encodePassword() { password ->
        "08a2d3c63bf9fc88276d97a9e8df5f841fd772724ad10f119f7e516f228b74c6"
    }

    ssService
}
  1. Any ideas on how I may be able to use the helper class while leaving all mocking in the unit tests only?
  2. Where would I best place the TestHelper.groovy file for using it in integration AND unit tests?

Note that everything is working perfectly fine when I move all helpers into UserTests.groovy directly!

like image 543
user569825 Avatar asked May 11 '11 20:05

user569825


2 Answers

The solution to this is to refrain from calling any user.save() in the TestHelper.groovy.

This makes sense, as for many (unit) tests a persisted (saved) instance is unnecessary anyways.

On the other hand many cases actually require an unsaved intance. (In order to test certain effects of the .save() itself, for example)

A working example for integration tests would be:

def user = TestHelper.getUserObject()
user.save()

For a unit tests:

def user = TestHelper.getUserObject()
user.springSecurityService = new SpringSecurityService() // or the described mock accordingly
user.save()

This keeps any mocks out of TestHelper.groovy

like image 60
user569825 Avatar answered Oct 25 '22 12:10

user569825


  1. In your TestHelper you can use Groovy ExpandoMetaClass metaClass.static to slap a mock closure for encodePassword on SpringSecurityService:

    SpringSecurityService.metaClass.'static'.encodePassword = {'08a2d3c63bf9fc88276d97a9e8df5f841fd772724ad10f119f7e516f228b74c6'}
    
  2. I would stick this class in a test package under src/groovy

like image 2
David Betts Avatar answered Oct 25 '22 12:10

David Betts