5

我正在尝试测试一个具有数据绑定的 Command 对象的控制器。

命令对象有一个注入其中的服务。

但是当我尝试测试命令对象时,永远找不到注入的服务方法,因为它永远不会“注入”

有没有办法在命令对象中模拟服务?

测试方法

void testLoginPasswordInvalid() {
    mockRequest.method = 'POST'
    mockDomain(User, [new User(login:"freddy", password:"realpassword")])
    mockLogging(UserService) // userService mocked
    MockUtils.prepareForConstraintsTests(LoginCommand)

    def userService = new UserService()
    def user = userService.getUser("freddy")//Gets called and returns the mockDomain
    assert userService.getUser("freddy")//Passes

    def cmd = new LoginCommand(login:"freddy", password:"letmein")
    cmd.validate() // Fails (userService is nevr injected)
    controller.login(cmd)
    assertTrue cmd.hasErrors()
    assertEquals "user.password.invalid", cmd.errors.password
    assertEquals "/store/index", renderArgs.view
}

未找到 userService 的 getUser() 方法

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

代码

被调用的控制器的登录方法,

def login = { LoginCommand cmd ->
  if(request.method == 'POST') {
     if(!cmd.hasErrors()){
       session.user = cmd.getUser()
       redirect(controller:'store')
     }
     else{
       render(view:'/store/index', model:[loginCmd:cmd])
     }
  }else{

    render(view:'/store/index')
  }
}

命令对象有一个“userService”注入其中。

验证器调用此 userService 来查找用户

 class LoginCommand {

    def userService

    String login
    String password

    static constraints = {
      login blank:false, validator:{ val, cmd ->
          if(!cmd.userService.getUser()){
             return "user.not.found"
          }
      }
 }

userService.getUser() 看起来像这样。

class UserService {

    boolean transactional = true

    User getUser(String login) {
        return User.findByLogin(login)

    }
}
4

2 回答 2

11

服务注入是使用 Spring autowire-by-name 完成的。(在 Grails 源代码树中autowire查找一个不错的代码片段,您可以使用它来在集成测试中为您自动装配控制器。)这仅在集成测试中起作用,其中有一个 Spring 应用程序上下文,其中包含可以被注入。

在单元测试中,你必须自己做这件事,因为你的东西周围没有 Spring-land。这可能会很痛苦,但会给您带来一些好处:

1) 很容易注入模拟版本的服务 - 例如,使用Expando- 以便更密切地指定控制器协作服务的行为,并允许您仅测试控制器逻辑而不是一起测试控制器和服务。(你当然也可以在单元测试中做后者,但是你可以选择如何连接它。)

2)它迫使你明确控制器的依赖关系——如果你依赖它,你的测试会显示它。这使它们成为控制器行为的更好规范。

3)您只能模拟您的控制器所依赖的外部协作者。这有助于您的测试不那么脆弱——当事情发生变化时不太可能需要改变。

简短的回答:您的测试方法需要cmd.userService = userService一行。

于 2009-11-10T04:56:58.387 回答
8

约翰所说的是对的。一个例子可能是:

def mockUsers = [new User(login:"freddy", password:"realpassword")]
mockDomain(User, mockUsers)

def userService = [getUser:{String login -> mockUsers[0]}] as UserService

def cmd = new LoginCommand (/*arguments*/)
cmd.userService = userService

您可以在http://groovy.codehaus.org/Groovy+Mocks查找其他模拟对象的方法

于 2009-11-10T13:24:16.323 回答