我在服务类中注册了方法,并尝试编写单元测试成功案例以防止重复发送电子邮件
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
我该如何解决这个问题?
[好吧,我仔细研究了您的代码,并在本地创建了许多虚拟类,以使其能够编译和运行,试图弄清楚您的用例是什么。实际上,您应该已经在MCVE中显示了,但是我想我现在有了主意。 (由于COVID-19,我很无聊,因为今天没有地方去见朋友。)>
我看到两个紧迫的问题:
您无法定义交互1 * entity.isPresent() >> false
,因为您的entity
不是模拟或间谍。此外,对Optional
使用私有构造函数以初始化entity
,这也很丑陋,在Groovy之外不起作用。此外,没有必要检查是否在可选对象上调用了isPresent()
,只需确保方法调用返回了false
。只需编写def entity = Optional.empty()
并删除交互,即可更轻松地实现。
正如我在评论中所说,只需确保DTO通过NullPointerException
或类似的密码设置,就可以摆脱加密程序调用的userDto.setPassword("pw")
。您的测试将如下所示:
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
}
}