Grails:如何对注入服务的命令对象进行单元测试

发布于 2024-08-10 13:11:20 字数 2022 浏览 3 评论 0原文

我正在尝试测试控制器 它有一个带有数据绑定的 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)

    }
}

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)

    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

蓝色星空 2024-08-17 13:11:20

服务注入是使用 Spring 按名称自动装配来完成的。 (Grep autowire 的 Grails 源代码树可以找到一个不错的代码片段,您可以使用它在集成测试中自动连接您的控制器。)这仅在集成测试中起作用,其中有一个 Spring 应用程序周围的上下文有可以注入的bean。

在单元测试中,您必须自己执行此操作,因为您的东西周围没有 Spring-land。这可能很痛苦,但给您带来了一些好处:

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

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

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

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

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.

飘逸的'云 2024-08-17 13:11:20

约翰说的很中肯。一个例子可能是:

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 查找模拟对象的其他方法+模拟

What John says is on the mark. One example might be:

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

You can lookup other ways to mock objects at http://groovy.codehaus.org/Groovy+Mocks

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文