如何在JUnit测试Java中替换Mockito以存根类

如何在JUnit测试Java中替换Mockito以存根类,java,spring,junit,mockito,stub,Java,Spring,Junit,Mockito,Stub,在JUnit的测试会话期间,我试图用Mockito的正确用法来代替存根类。 不幸的是,在web上有很多关于Mockito的教程,但是关于存根方法的教程很少,我想学习这项技术 本测试由Mockito进行: @Test public void addWrongNewUserSpaceInUsername() throws Exception { when(userValidator.isValidUsername(user.getUsername())).thenReturn(false

在JUnit的测试会话期间,我试图用Mockito的正确用法来代替存根类。 不幸的是,在web上有很多关于Mockito的教程,但是关于存根方法的教程很少,我想学习这项技术

本测试由Mockito进行:

 @Test
public void addWrongNewUserSpaceInUsername() throws Exception {

    when(userValidator.isValidUsername(user.getUsername())).thenReturn(false);

    try {
        mockMvc.perform(
                post("/register")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(asJsonString(user)
                        ));
    } catch (Exception e) {
        Assert.assertTrue(e.getCause() instanceof UsernameNotValidException);
    }
}
为了澄清这些问题,涉及的类别如下:

1) 控制器

   @RestController
public class UserController {

    @Autowired
    RepositoryUserDB repositoryUserDB;

    @Autowired
    UserValidator userValidator;

    @RequestMapping(value = "/register", method = RequestMethod.POST)
    public User createUser(@RequestBody User user) {


        if (userValidator.isValidUsername(user.getUsername())) {

            if(!userValidator.isValidPassword(user.getPassword())){
                throw new PasswordNotValidException();
            }

            if(userValidator.isValidDateOfBirth(user.getDateOfBirth()) == false){
                throw new DOBNotValidException();
            }

            // se lo user e' gia' presente
            if (repositoryUserDB.getUserByUsername(user.getUsername()) == null) {
                return repositoryUserDB.createUser(user);
            }
            throw new UsernameAlreadyExistException();
        } else throw new UsernameNotValidException();

    }
}
2) 回购的界面:

public interface RepositoryUserDB {

    User getUserByUsername(String username);

    User createUser(User user);
}
3) 回购:

5) RecentyExceptionHandler

    @ControllerAdvice
public class RestEntityExceptionHandler {

    @ExceptionHandler(UsernameNotValidException.class)
    @ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "username wrong")
    public void handleUsernameException() {
    }

    @ExceptionHandler(UsernameAlreadyExistException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "the username is already presents")
    public void handleUsername() {
    }

    @ExceptionHandler(PasswordNotValidException.class)
    @ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "password wrong")
    public void handlePasswordException() {
    }

    @ExceptionHandler(DOBNotValidException.class)
    @ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "Date Of Birth wrong")
    public void handleDOBException(){
    }

}

理论上,对于您的用例,存根方法应该非常简单。
但是,由于您依赖于使用SpringBeans容器的SpringBoot测试,设置起来非常困难,因为您应该找到一种方法将模拟bean注入容器中,以替换实际的bean:
UserValidator

Spring启动测试中的模拟通常依赖于。
这不是一个mockito mock,但不是很远。
要了解Mockito mock的区别,您可以参考。

使用框架提供了许多现成的功能,但也限制了您自己,因为您想绕过框架功能。


假设您没有使用Spring Boot执行集成测试,而是执行真正的单元测试(因此没有Spring Boot容器),那么事情就可以这样执行。

您没有模拟
UserValidator.isValidUsername()
,而是定义了
UserValidator
的自定义实现,它按照测试中的预期存根方法返回。
最后,Mockito或任何mock框架为您做了什么。

下面是存根类:

public class UserValidatorStub extends UserValidator {

    private String expectedUsername;
    private boolean isValidUsername;

    public UserValidatorStub(String expectedUsername, boolean isValidUsername){
          this.expectedUsername = expectedUsername;
          this.isValidUsername = isValidUsername;
    }
    public boolean isValidUsername(String username) {
        if (username.equals(expectedUsername)){
           return isValidUsername;
        }
        // as fallback, it uses the default implementation but you may return false or null as alternative
       return super.isValidUsername(username);
    }

}
它接受一个构造函数来存储传递给存根方法的预期参数和要返回的存根结果。

现在,这里是如何编写测试的:

 @Test
public void addWrongNewUserSpaceInUsername() throws Exception {

    // inject the mock in the class under test
    UserController userController = new UserController(new UserValidatorStub(user.getUsername(), false));
    try {
        userController.createUser(user);
    } catch (Exception e) {
        Assert.assertTrue(e.getCause() instanceof UsernameNotValidException);
    }
}

请注意,该示例依赖于
UserController
中的构造函数注入来设置依赖项。

如何将验证注入控制器?@MaciejKowalski:由使用ControllerAdvice的类执行。你需要它吗?查看测试的完整设置会很有用OK我将编辑我的问题并添加class@MaciejKowalski当前位置完成。你的干预非常宝贵。你的问题非常有趣。在某些特定情况下,我使用stub代替mock,因为mock特性缺少一些东西来实现预期的验收测试。不幸的是,由于框架方法的原因,有时设置起来非常困难。所以,我喜欢你的问题!谢谢@davidxxx,我正在努力对Java语言更有信心。我只是一名初级开发人员:),我必须学会高效。
public class UserValidatorStub extends UserValidator {

    private String expectedUsername;
    private boolean isValidUsername;

    public UserValidatorStub(String expectedUsername, boolean isValidUsername){
          this.expectedUsername = expectedUsername;
          this.isValidUsername = isValidUsername;
    }
    public boolean isValidUsername(String username) {
        if (username.equals(expectedUsername)){
           return isValidUsername;
        }
        // as fallback, it uses the default implementation but you may return false or null as alternative
       return super.isValidUsername(username);
    }

}
 @Test
public void addWrongNewUserSpaceInUsername() throws Exception {

    // inject the mock in the class under test
    UserController userController = new UserController(new UserValidatorStub(user.getUsername(), false));
    try {
        userController.createUser(user);
    } catch (Exception e) {
        Assert.assertTrue(e.getCause() instanceof UsernameNotValidException);
    }
}