首页 > 解决方案 > Spock 单元测试使用 spring 数据导致 Optional 中的错误

问题描述

我开始在 Spring Boot + Spring Data 项目中使用 Spock 框架执行测试。

当我尝试模拟我的存储库时会出现问题,但特别是在某些方法中返回是可选的。

Cannot invoke method orElse() on null object
java.lang.NullPointerException: Cannot invoke method orElse() on null object
    at br.com.moskit.jivochat.service.UserService.getResponsible(UserService.groovy:37)
    at br.com.moskit.jivochat.service.UserServiceTest.Retrive the responsible of interaction in JivoChat by configs of plugin item(UserServiceTest.groovy:65)

我的测试实现:

class UserServiceTest extends Specification {

    UserService userService

    void setup() {
        userService = new UserService()
        userService.userRepository = Mock(UserRepository)
        GroovyMock(Optional)
    }


    def "Retrive the responsible of interaction in JivoChat by configs of plugin item"() {

        given: 'that exist a collection of JivoChat interaction configurations'

        List<Map> agents = null

        Map configs = [responsibleId: 1]

        userService.userRepository.findById(_) >> Optional.of(new User(username: "XPTO")).orElse("null")

        when: 'the main method is called'

        User user = userService.getResponsible(configs, agents)

        then: 'the method get last agent and search in DB by e-mail'
        1 * userService.userRepository.findById(_)
    }
}

我的方法:

    User getResponsible(Map configs, List<Map> agents) {

        //Ommited...

        Integer responsibleId = configs.responsibleId as Integer
        Optional<User> userOptional = userRepository.findById(responsibleId)
        User user = userOptional.orElse(null)

        user
    }

标签: unit-testinggroovyoptionalspock

解决方案


这是一个经典的答案,答案可以在Spock 手册章节“Combining Mocking and Stubbing”中找到:

注意:同一方法调用的模拟和存根必须在同一交互中发生。

所以解决方案如下所示:

package de.scrum_master.stackoverflow.q66208875

class User {
  int id
  String username
}
package de.scrum_master.stackoverflow.q66208875

class UserRepository {
  Optional<User> findById(int id) {
    Optional.of(new User(id: id, username: "User #$id"))
  }
}
package de.scrum_master.stackoverflow.q66208875

class UserService {
  UserRepository userRepository = new UserRepository()

  User getResponsible(Map configs, List<Map> agents) {
    Integer responsibleId = configs.responsibleId as Integer
    Optional<User> userOptional = userRepository.findById(responsibleId)
    User user = userOptional.orElse(null)
    user
  }
}
package de.scrum_master.stackoverflow.q66208875

import spock.lang.Specification

class UserServiceTest extends Specification {
  UserService userService

  void setup() {
    userService = new UserService()
    userService.userRepository = Mock(UserRepository)
  }

  def "retrieve the responsible of interaction in JivoChat by configs of plugin item"() {
    given: 'that exist a collection of JivoChat interaction configurations'
    List<Map> agents = null
    Map configs = [responsibleId: 1]

    when: 'the main method is called'
    User user = userService.getResponsible(configs, agents)

    then: 'the method get last agent and search in DB by e-mail'
    1 * userService.userRepository.findById(_) >> Optional.of(new User(username: "XPTO"))//.orElse("null")
  }
}

看看我是如何在一行中结合存根方法结果和验证模拟交互的?

你也不需要任何GroovyMock(Optional),无论那是什么意思。您还需要确保获得findById(_)正确的结果类型并删除 false .orElse("null")


推荐阅读