【问题标题】:How to use mockito to not evaluate a method inside the testing method如何使用 mockito 不评估测试方法中的方法
【发布时间】:2018-01-12 06:17:50
【问题描述】:


我正在为控制器方法实现一个测试用例。控制器方法如下所示,

public class LoginController{
   public String register(String token){
     //some logic 
     loginService.delete(String token);
    //some logic
   return "xxxx";
   }
}

我正在实施测试用例来测试 register 方法,我不希望对方法 delete 进行评估。 (delete 方法是一个返回 void 的服务方法)。我做了一些研究,并在我的测试方法中使用了下面的代码来不评估删除方法,但是当我调试它时,它仍然进入了删除方法。谁能指出我做错了什么。

public class LoginControllerTest{
   private loginService loginServiceMock;

   @Test
   public void testRegister(){
      loginServiceMock = new loginServiceImpl();
      loginService spy = spy(loginServiceMock);
      doNothing().when(spy).delete(any(String.class));
      //calling the controller method 
   }
}

【问题讨论】:

  • 顺便说一句。 'org.mockito.Matchers' 中有 anyString() 匹配器。使用它而不是任何(String.class)。更干净,更快。您可以在 sourceCode 中检查 returnString() 比 org.mockito.internal.progress.HandyReturnValues 中的 returnFor(Class clazz) 简单得多
  • @Anna,delete 类的 delete 方法是最终的吗?还是课程本身是最终的?这两个条件中的任何一个都会阻止该方法以您想要的方式被存根。
  • @DavidWallace 没有一个是最终的
  • 好的,你如何将你的间谍对象注入LoginController?您能否展示创建LoginController 并将LoginService 添加到其中的测试部分?
  • 看看 Mockito 中的 never() 方法

标签: java unit-testing mockito spy


【解决方案1】:

LoginController 重构为类似

public class LoginController {
    private LoginService loginService;

    public LoginController(LoginService loginService) {
        this.loginService = loginService;
    }

    public String register(String token){
        //some logic 
        loginService.delete(token);
        //some logic
        return "xxxx";
    }
 }

 public interface LoginService {
     void delete(String token);
 }

然后在你的测试中

public class LoginControllerTest {
   private LoginController loginController;

   @Test
   public void testRegister(){
      loginController = new LoginController(t -> {});

      loginController.register("foo");

      //do some assertion
   }
}

我知道这不是您(也许)期望的那种解决方案,但它解决了您的问题(不再调用真正的 delete)。

此解决方案的其他优点:

  • 代码更解耦,更易维护
  • 上面的直接结果:代码变得更容易测试
  • 上面的直接结果:您不需要再做需要模拟的复杂事情了

【讨论】:

    【解决方案2】:

    只要将loginService spy 对象注入到您正在测试的LoginController 中,您所做的就应该有效。从您发布的代码中看不到这一点。

    有两个原因会使控制器调用真正的登录服务方法:

    1. 您忘记将间谍注入控制器:类似于loginControllerToTest = new LoginController(spy)loginControllerToTest.setLoginService(spy)

    2. loginService.delete() 是一种静态方法,在这种情况下,您需要重构代码以删除静态依赖项或使用其他模拟工具,例如 powermock。详情请见this question

    【讨论】:

      【解决方案3】:

      spy 的整个想法是,它允许您在实际实例上调用和验证方法。因此,如果您调用一个间谍实例方法,它实际上会在实际实例上调用该方法,除非它被模拟。

      对于您的情况,您需要使用mock 而不是spy

      @RunWith(MockitoJUnitRunner.class)
      public class LoginControllerTest{
      
         @InjectMocks
         private LoginController controller;
      
         @Mock
         private loginService loginServiceMock; 
      
      
         @Test
         public void testRegister(){      
      
            doNothing().when(loginServiceMock).delete(anyString()));
            //calling the controller method 
            String value = controller.register("mytoken");
            verify(loginServiceMock,times(1)).delete(anyString());
         }
      }
      

      【讨论】:

      • 我试过了,但是在测试注册方法时,它仍然进入了删除方法:(
      • @Anna 你能更新你的完整测试类吗,你有没有用@RunWith(MockitoJUnitRunner.class) 注释你的测试类?
      • 是的,我有。我的其他测试运行良好。该类非常庞大,这就是为什么我简化并仅添加了必要的代码 sn-ps
      猜你喜欢
      • 2015-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-22
      • 2018-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多