圣杯。测试带有 spring bean 的命令对象

Grails. Testing command objects with spring beans inside

我有一个带有约束的命令对象,在自定义验证器中使用 spring bean:

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

但是在单元测试中调用 new UserPasswordCommand(...) 时,我得到以下信息:

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

所以看起来 springSecurityService = null(正如预期的那样)。我尝试了不同的操作来模拟或 "metaClass" 但没有成功。

请告知是否有更好的方法在命令对象中使用 applicationContext 中的 bean 或在 Holders.applicationContext 中使用一些模拟 bean 的方法。

提前致谢!

更新

将以下内容放入 setup() 部分:

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

但是没有效果。 spring从 Holders 检索到的 applicationContext 中的 null。我做错了什么?

您可以像这样覆盖 Holder.applicationContext:

  def setup() {
    ApplicationContext appContext = Mock()
    PasswordEncoder passwordEncoder = Mock()
    passwordEncoder.isPasswordValid(_, _, _) >> true

    appContext.passwordEncoder >> passwordEncoder //you can do the same for springSecurityService

    //override Holder.getAplicationContext() method to return mocked context
    Holders.metaClass.static.applicationContext = { appContext }
}

我通过删除原始命令对象中的 Holders 解决了这个问题:

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

将 mocks/stubs 添加到测试测试:

def springSecurityService = Mock(SpringSecurityService)
def passwordEncoder = Mock(PasswordEncoder)

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

和:

given:
Map props = [
    currentPassword: CURRENT_PASSWORD,
    passwordEncoder: passwordEncoder,
    springSecurityService: springSecurityService,
]

expect:
new UserPasswordCommand(props).validate()