如何对使用springSecurityService的控制器进行单元化?

Mat*_*t L 9 grails unit-testing mocking spring-security

我有这样的用户类:

class User {
    transient springSecurityService
    String displayName
    String password
<snip>
    protected void encodePassword() {
        password = springSecurityService.encodePassword(password)
    }
}
Run Code Online (Sandbox Code Playgroud)

还有一个UserController.我正在尝试编写单元测试UserController但是我收到此错误用于保存,更新和删除测试:

java.lang.NullPointerException: Cannot invoke method encodePassword() on null object
Run Code Online (Sandbox Code Playgroud)

我有什么方法可以通过嘲讽来实现这一点?

我已经尝试了许多模拟代码的组合,如下所示,但我不知所措.

defineBeans {
    springSecurityService(SpringSecurityService)
}
Run Code Online (Sandbox Code Playgroud)

任何建议将不胜感激.

Jar*_*son 8

我个人不喜欢在生产代码中添加逻辑来帮助满足测试要求.有时你必须决定什么是最好的.几个选项......

  1. 上面的答案是有效的,但正如我所说,我个人不喜欢
  2. 不要进行单元测试.将所有遇到这种情况的测试写成集成测试.
  3. 用假服务嘲笑它.

如果这个代码(或遇到相同问题的代码)散布在整个应用程序中,那么你可能想要找出一种方法来模拟所有测试用例的单元测试中的这些调用,这样你就不会重复设置到处努力.模拟这个的简单方法是使用metaClassing.

@Test
public void something() {
   def user = ...
   def springSecurityService = new Object()
   springSecurityService.metaClass.encodePassword = {String password -> "ENCODED_PASSWORD"}
   user.springSecurityService = springSecurityService
   ...
}
Run Code Online (Sandbox Code Playgroud)

现在,当springSecurityService.encodePassword调用gets 时,它应该返回"ENCODED_PASSWORD".我也创建了一个Object而不是实例化一个新的,SpringSecurityService因为如果你实例化一个实际的服务,那么你最终可能会意外地在不知不觉中调用该服务上的实际方法,并且你的测试通过了错误的原因.我宁愿得到一个没有这样的方法错误,而不是通过不应该通过的测试.


Tom*_*ski 0

我是这样做的:

    protected void encodePassword() {
        // SpringSecutiryService is not injected in tests.
        if (springSecurityService)
            password = springSecurityService.encodePassword(formPassword)
    }
Run Code Online (Sandbox Code Playgroud)

  • 这不是一个理想的解决方案,看起来更像是一个让测试通过的修复方案。如果安全服务在生产中由于某些奇怪的原因而被取消怎么办...那么您的所有密码都不会被散列(没有错误消息) (2认同)