Grails:你如何单元测试一个注入了服务的命令对象 [英] Grails: How do you unit test a command object with a service injected into it

查看:140
本文介绍了Grails:你如何单元测试一个注入了服务的命令对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图测试一个具有带数据绑定的Command对象的
控制器。
$ b

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

p>

但是,当我尝试测试命令对象时,注入的服务方法
永远不会被发现,因为它从未被注入过。



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



测试方法

  void testLoginPasswordInvalid(){
mockRequest.method ='POST'
mockDomain(User,[new User(login:freddy,password :truepassword)])
mockLogging(UserService)// userService mocked
MockUtils.prepareForConstraintsTests(LoginCommand)
$ b $ def userService = new UserService()
def user = userService.getUser(freddy)//获取调用并返回mockDomain
断言userService.getUser(freddy)//通过

def cmd = new LoginCommand(login: freddy,密码:letmein)
cmd.valida te()//失败(userService被nevr注入)
controller.login(cmd)
assertTrue cmd.hasErrors()
assertEqualsuser.password.invalid,cmd.errors.password
assertEquals/ store / index,renderArgs.view
}

未找到userService的getUser()方法

 无法在空对象
java上调用getUser()方法。 lang.NullPointerException:无法在null对象上调用方法getUser()

代码



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

  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')
}
}

Command对象中注入了一个userService。
$ b

验证器调用此用户服务来查找用户

  class LoginCommand {

def userService

字符串登录
字符串密码

static constraints = {
login blank:false,验证器:{val,cmd - >
if(!cmd.userService.getUser()){
returnuser.not.found
}
}
}

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

  class UserService {

布尔transactional = true

用户getUser(字符串登录){
返回User.findByLogin(登录)




解决方案

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



在单元测试中,你必须自己做这件事,因为你的周围没有Spring-land东东。这可能是一个痛苦,但给你一些好处:
$ b $ 1)很容易注入模拟版本的服务 - 例如,使用 Expando - 为了更密切地指定控制器协作服务的行为,并允许您只测试控制器逻辑而不是控制器和服务。 (你当然也可以在单元测试中做后者,但你可以选择如何连接它。)



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



3)您只能模拟您的控制器依赖的外部协作者。这有助于你的测试变得更脆弱 - 当事情发生变化时不太可能需要改变。 简短回答:你的测试方法需要一个 cmd.userService = userService 行。


I am trying to test a Controller that has a Command object with data binding.

The Command Object has a Service injected into it.

But When I try test the command object the injected service method is never found as it is never "injected"

Is there a way to mock a service inside a command object?

Test method

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
}

The getUser() method of the userService isn't found

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

Code

The login method of the controller being called,

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')
  }
}

The Command Object has a "userService" injected into it.

The validator calls this userService to find a user

 class LoginCommand {

    def userService

    String login
    String password

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

The userService.getUser() looks like this.

class UserService {

    boolean transactional = true

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

    }
}

解决方案

Service injection is done using Spring autowire-by-name. (Grep the Grails source tree for autowire to find a nice code fragment you can use to get it to autowire your controllers for you in integration tests.) This only functions in integration tests, where there's a Spring application context around that has the beans that can be injected.

In unit tests, you have to do this yourself since there's no Spring-land surrounding your stuff. This can be a pain, but gives you some benefits:

1) It's easy to inject mock versions of services - for example, using an Expando - in order to more closely specify the behavior of your controller's collaborating services, and to allow you to test only the controller logic rather than the controller and service together. (You can certainly do the latter in a unit test as well, but you have the choice of how to wire it up.)

2) It forces you to be explicit about the dependencies of your controller - if you depend on it, your tests will show it. This makes them a better specification for the behavior of your controller.

3) You can mock only the pieces of external collaborators your controller depends on. This helps your tests be less fragile - less likely to need to change when things change.

Short answer: your test method needs a cmd.userService = userService line.

这篇关于Grails:你如何单元测试一个注入了服务的命令对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆