如何只测试使用spock在测试类中编写的部分测试方法的一部分

问题描述 投票:0回答:1

我在服务类中注册了方法,并尝试编写单元测试成功案例以防止重复发送电子邮件

public void signUp(UserDTO userDTO) {
        logger.info("ActionLog.Sign up user.Start");
        Optional<UserEntity> checkedEmail = userRepository.findByEmail(userDTO.getEmail());
        System.out.println(checkedEmail);
        if (checkedEmail.isPresent()) {
            System.out.println("check email: "+checkedEmail);
            logger.error("ActionLog.WrongDataException.Thrown");
            throw new WrongDataException("This email already exists");
        }

        String password = new BCryptPasswordEncoder().encode(userDTO.getPassword());
        UserEntity customerEntity = UserEntity
                .builder()
                .name(userDTO.getName())
                .surname(userDTO.getSurname())
                .username(userDTO.getEmail())
                .email(userDTO.getEmail())
                .password(password)
                .role(Role.ROLE_USER)
                .build();

        userRepository.save(customerEntity);
        logger.info("ActionLog.Sign up user.Stop.Success");

    }

这是我的测试课

class UserServiceImplTest extends Specification {

    UserRepository userRepository
    AuthenticationServiceImpl authenticationService
    UserServiceImpl userService

    def setup() {
        userRepository = Mock()
        authenticationService = Mock()
        userService = new UserServiceImpl(userRepository, authenticationService)
    }

    def "doesn't throw exception if email doesn't exist in database"() {

        given:
        def userDto = new UserDTO()
        def entity = new Optional<UserEntity>()
        userDto.setEmail("[email protected]")
        1 * userRepository.findByEmail(userDto.getEmail()) >> entity
        1 * entity.isPresent() >> false

        when: "send dto object to service "
        userService.signUp(userDto)


        then: ""
        notThrown(WrongDataException)
    }


}

测试失败,因为它为我提供了ByCryptPasswordEncoder的NPE:但我不编写集成测试,而只需要测试重复的电子邮件成功和失败案例

java.lang.NullPointerException
    at org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder.encode(BCryptPasswordEncoder.java:108)
    at az.gdg.msauth.service.impl.UserServiceImpl.signUp(UserServiceImpl.java:41)
    at az.gdg.msauth.service.UserServiceImplTest.doesn't throw exception if email doesn't exist in database(UserServiceImplTest.groovy:35)

但是,我在服务等级中评论了这些

String password = new BCryptPasswordEncoder().encode(userDTO.getPassword());
        UserEntity customerEntity = UserEntity
                .builder()
                .name(userDTO.getName())
                .surname(userDTO.getSurname())
                .username(userDTO.getEmail())
                .email(userDTO.getEmail())
                .password(password)
                .role(Role.ROLE_USER)
                .build();

        userRepository.save(customerEntity);

它给了我

Too few invocations for:

1 * entity.isPresent() >> false   (0 invocations)

Unmatched invocations (ordered by similarity):

None


Too few invocations for:

1 * entity.isPresent() >> false   (0 invocations)

Unmatched invocations (ordered by similarity):

None

我该如何解决这个问题?

java unit-testing groovy spock
1个回答
0
投票

[好吧,我仔细研究了您的代码,并在本地创建了许多虚拟类,以使其能够编译和运行,试图弄清楚您的用例是什么。实际上,您应该已经在MCVE中显示了,但是我想我现在有了主意。 (由于COVID-19,我很无聊,因为今天没有地方去见朋友。)>

我看到两个紧迫的问题:

  1. 您无法定义交互1 * entity.isPresent() >> false,因为您的entity不是模拟或间谍。此外,对Optional使用私有构造函数以初始化entity,这也很丑陋,在Groovy之外不起作用。此外,没有必要检查是否在可选对象上调用了isPresent(),只需确保方法调用返回了false。只需编写def entity = Optional.empty()并删除交互,即可更轻松地实现。

  2. 正如我在评论中所说,只需确保DTO通过NullPointerException或类似的密码设置,就可以摆脱加密程序调用的userDto.setPassword("pw")。您的测试将如下所示:

  3. package de.scrum_master.stackoverflow.q60884910
    
    import spock.lang.Specification
    
    class UserServiceImplTest extends Specification {
      UserRepository userRepository
      AuthenticationServiceImpl authenticationService
      UserServiceImpl userService
    
      def setup() {
        userRepository = Mock()
        authenticationService = Mock()
        userService = new UserServiceImpl(userRepository, authenticationService)
      }
    
      def "doesn't throw exception if email doesn't exist in database"() {
        given:
        def userDto = new UserDTO()
        def entity = Optional.empty()
        userDto.setEmail("[email protected]")
        userDto.setPassword("pw")
        1 * userRepository.findByEmail(userDto.getEmail()) >> entity
    //    1 * entity.isPresent() >> false
    
        when: "send dto object to service "
        userService.signUp(userDto)
    
        then: ""
        notThrown(WrongDataException)
      }
    
    }
    

我还认为没有必要实际调用userRepository.findByEmail(..)并且使用特定参数来调用它。我认为这是单元测试的超规范。更改被测方法的内部实现时,您必须对其进行调整。我认为仅指定存根结果就足够了。如果您还重新组织了一下代码,则测试如下所示:

package de.scrum_master.stackoverflow.q60884910

import spock.lang.Specification

class UserServiceImplTest extends Specification {
  UserRepository userRepository = Mock()
  AuthenticationServiceImpl authenticationService = Mock()
  UserServiceImpl userService = new UserServiceImpl(userRepository, authenticationService)

  def "doesn't throw exception if email doesn't exist in database"() {
    given: "a user DTO"
    def userDto = new UserDTO()
    userDto.email = "[email protected]"
    userDto.password = "pw"

    and: "a user repository not finding any user by e-mail"
    userRepository.findByEmail(_) >> Optional.empty()

    when: "signing up a new user"
    userService.signUp(userDto)

    then: "no duplicate e-mail address exception is thrown"
    notThrown WrongDataException
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.