springunit-testinggrailsspockcommand-objects

Grails. Testing command objects with spring beans inside


I have a command object with constraints using spring beans in custom validator:

class UserPasswordCommand {
    String currentPassword
    //other fields....
    static constraints = {
        currentPassword validator: { currPass, upc ->
            Holders.applicationContext.passwordEncoder.isPasswordValid(
                    Holders.applicationContext.springSecurityService.currentUser.password, currPass, null)
        }
    }
}

But when invoking new UserPasswordCommand(...) in unit test I get the following:

java.lang.NullPointerException: Cannot get property 'currentUser' on null object

So it appears that springSecurityService = null (as expected). I tried different actions to mock or "metaClass" it but unsuccessful.

Please advise if there is a better way to use beans from applicationContext in command objects or some approaches of mocking beans in Holders.applicationContext.

Thanks in advance!

UPDATE

Placed the following to setup() section:

def setup() {
    def appContext = Mock(ApplicationContext)
    def springSecurityService = Mock(SpringSecurityService)
    appContext.springSecurityService >> springSecurityService
    Holders.metaClass.static.applicationContext = { appContext }
}

But no effect. springSecurityService is null in applicationContext retrieved from Holders. What am I doing wrong?


Solution

  • I resolved the issue by getting rid of Holders in the original command object:

    class UserPasswordCommand {
        static passwordEncoder
        static springSecurityService
        String currentPassword
        //...
        static constraints = {
            currentPassword validator: { currPass, upc ->
        passwordEncoder.isPasswordValid(springSecurityService.currentUser.password, currPass, null)
            }
        //...
        }
    }
    

    Added the mocks/stubs to test test:

    def springSecurityService = Mock(SpringSecurityService)
    def passwordEncoder = Mock(PasswordEncoder)
    
    def setup() {
        passwordEncoder.isPasswordValid(_, _, _) >> Boolean.TRUE
        springSecurityService.currentUser >> Mock(User)
    }
    

    and:

    given:
    Map props = [
        currentPassword: CURRENT_PASSWORD,
        passwordEncoder: passwordEncoder,
        springSecurityService: springSecurityService,
    ]
    
    expect:
    new UserPasswordCommand(props).validate()