我正在尝试在项目中进行单元测试,但我一直在尝试模拟 BCrypt.checkpw 函数,我读到制作静态模拟需要某种方式来处理测试,我是不完全确定是否是这种情况或者可能是另一回事。
UsuarioController.java
@GetMapping("lastPassword")
public ResponseEntity<Object> lastPassword(@RequestParam(required = true) String correo, @RequestParam(required = true) String newPassword) { // Checa si la contraseña ingresada fue la ultima contraseña del usuario
try {
Optional<usuarioVista> tmp = repositoryVista.findByCorreo(correo);
Optional<Usuario> usuario = service.findById(Integer.toUnsignedLong(tmp.get().getIdUsuario())); // Se busca al usuario perteneciente al correo recibido
String password = "$2a$14$" + usuario.get().getPassword(); // Se recoge la contraseña encriptada
Boolean isLast = BCrypt.checkpw(newPassword, password); // Se checa si la contraseña ingresada coincide con la encriptada
return new ResponseEntity<>(isLast, HttpStatus.OK);
} catch (Exception e) {
return ResponseEntity.badRequest().build();
}
}
UsuarioControllerTest.java
@Test
void lastPassword() throws Exception {
String correo = "[email protected]";
String newPassword = "Password123@";
usuarioVista usuario = new usuarioVista();
Usuario usuarioOption = mock(Usuario.class);
Optional<usuarioVista> usuarioCorreo = Optional.of(usuario);
Optional<Usuario> usuarioId = Optional.of(usuarioOption);
BCrypt encriptado = mock(BCrypt.class);
String password = "$2a$14$9SXpfOnx/waQlmHn6L3yp.L5d7wlf.ZMqVIwaDHuq8KDhppWtHi9K";
Boolean isLast = true;
Mockito.when(repositoryVista.findByCorreo(correo)).thenReturn(usuarioCorreo);
Mockito.when(usuarioService.findById(33L)).thenReturn(usuarioId);
Mockito.when(usuarioOption.getPassword()).thenReturn(newPassword);
when(encriptado.checkpw(newPassword, password)).thenReturn(isLast);
ResponseEntity<Object> response = controller.lastPassword(correo, newPassword);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
堆栈跟踪
%TESTC 1 v2
%TSTTREE2,mx.com.ficachi.master.login.controllers.UsuarioControllerTest,true,1,false,1,UsuarioControllerTest,,[engine:junit-jupiter]/[class:mx.com.ficachi.master.login.controllers.UsuarioControllerTest]
%TSTTREE3,lastPassword(mx.com.ficachi.master.login.controllers.UsuarioControllerTest),false,1,false,2,lastPassword(),,[engine:junit-jupiter]/[class:mx.com.ficachi.master.login.controllers.UsuarioControllerTest]/[method:lastPassword()]
%TESTS 3,lastPassword(mx.com.ficachi.master.login.controllers.UsuarioControllerTest)
%ERROR 3,lastPassword(mx.com.ficachi.master.login.controllers.UsuarioControllerTest)
%TRACES
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
at mx.com.ficachi.master.login.controllers.UsuarioControllerTest.lastPassword(UsuarioControllerTest.java:148)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
%TRACEE
%TESTE 3,lastPassword(mx.com.ficachi.master.login.controllers.UsuarioControllerTest)
%RUNTIME8003
我尝试模拟 BCrypt 对象,使用 @MockBean 注释,但它引发了同样的错误。
你不太了解模拟是如何工作的。模拟不是针对类,而是针对类的特定对象。另外,模拟静态方法并不是一个好主意,mockito 无法做到这一点。如果您需要模拟 Bcrypt 类,我将创建一个单独的服务,在其中放置此逻辑并模拟服务本身。在你的情况下,你甚至不需要创建一个服务,因为 spring 已经有了 BcryptPasswordEncoder。
示例:
@RestController
public YourControllerName {
private final PasswordEncoder passwordEncoder;
public YourControllerName(PasswordEncoder encoder) {
this.passwordEncoder = encoder;
}
@GetMapping("lastPassword")
public ResponseEntity<Object> lastPassword(@RequestParam(required = true) String correo, @RequestParam(required = true) String newPassword) { // Checa si la contraseña ingresada fue la ultima contraseña del usuario
try {
Optional<usuarioVista> tmp = repositoryVista.findByCorreo(correo);
Optional<Usuario> usuario = service.findById(Integer.toUnsignedLong(tmp.get().getIdUsuario())); // Se busca al usuario perteneciente al correo recibido
String password = "$2a$14$" + usuario.get().getPassword(); // Se recoge la contraseña encriptada
Boolean isLast = passwordEncoder.matches(newPassword, password); // Se checa si la contraseña ingresada coincide con la encriptada
return new ResponseEntity<>(isLast, HttpStatus.OK);
} catch (Exception e) {
return ResponseEntity.badRequest().build();
}
}
要使用PasswordEncoder,您需要定义一个bean:
@Configuration
puclic class YourConfig {
@Bean
puclic PasswordEncoder passwordEncoder() {
return new BcryptPasswordEncoder();
}
}
测试将如下所示:
@Autowired
private YourControllerName controller;
@MockBean
private PasswordEncoder passwordEncoder
@Test
void lastPassword() throws Exception {
String correo = "[email protected]";
String newPassword = "Password123@";
usuarioVista usuario = new usuarioVista();
Usuario usuarioOption = mock(Usuario.class);
Optional<usuarioVista> usuarioCorreo = Optional.of(usuario);
Optional<Usuario> usuarioId = Optional.of(usuarioOption);
String password = "$2a$14$9SXpfOnx/waQlmHn6L3yp.L5d7wlf.ZMqVIwaDHuq8KDhppWtHi9K";
Boolean isLast = true;
Mockito.when(repositoryVista.findByCorreo(correo)).thenReturn(usuarioCorreo);
Mockito.when(usuarioService.findById(33L)).thenReturn(usuarioId);
Mockito.when(usuarioOption.getPassword()).thenReturn(newPassword);
when(passwordEncoder.matches(newPassword, password)).thenReturn(isLast);
ResponseEntity<Object> response = controller.lastPassword(correo, newPassword);
assertEquals(HttpStatus.OK, response.getStatusCode());
}