2009-11-09 41 views
5

我想测试一个控制器 有一个Command对象的数据绑定。Grails:你如何单元测试一个命令对象与一个服务注入到它

命令对象有一个服务注入到它。

但是当我尝试测试命令对象注入的服务方法 是从来没有发现,因为它永远不会“注入”

有没有办法来模拟一个命令对象内的服务?

试验方法

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) 

    } 
} 

回答

11

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

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

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

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

3)你只能嘲笑控制器依赖的外部协作者。这有助于您的测试不易脆弱 - 当事情发生变化时不太可能需要更改。

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

+0

谢谢,这个工作一种享受 ,现在我明白了更多 – Daxon 2009-11-12 14:41:54

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查找等方式来mock对象

相关问题