【问题标题】:Is it possible to verify arbitrary interaction using Mockito in a compact way?是否可以以紧凑的方式使用 Mockito 验证任意交互?
【发布时间】:2012-06-26 21:02:42
【问题描述】:

在 Mockito 中很容易验证特定交互(特定方法调用)发生在模拟对象上,并且有 verifyZeroInteractions() 用于检查根本没有发生交互。假设我正在使用 info()warn()error() 等方法测试记录器等接口。在特定场景中,我知道应该调用其中一个方法,但我不知道真的很在乎哪一个。是否有一种紧凑的方法来检查是否发生了与模拟对象的任何交互,而无需指定应该调用哪个确切的方法?或者也许这种机制是不必要的,因为测试它的“Mockito 方式”与我想象的不同?

【问题讨论】:

    标签: methods mocking call mockito


    【解决方案1】:

    使用 log4j,为了测试记录器,我做了以下设置:

    @Mock private Appender log4jAppender;
    private Logger logger;
    
    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    
        Logger root = Logger.getRootLogger();
        if (!root.getAllAppenders().hasMoreElements()) {
            // No appenders means log4j is not initialized!
            BasicConfigurator.configure();
        }
        logger = Logger.getLogger(MyClassWhichLogs.class);
        logger.addAppender(log4jAppender);
    }
    

    然后在我的测试中我执行以下操作:

    verifyZeroInteractions(log4jAppender);
    

    verify(log4jAppender).doAppend(any(LoggingEvent.class);
    

    如果您需要测试记录的值,您可以提供一个捕获器:

    ArgumentCaptor<LoggingEvent> logCaptor = ArgumentCaptor.forClass(LoggingEvent.class);
    verify(log4jAppender).doAppend(logCaptor.capture());
    assertTrue(logCaptor.getValue().contains("my text to match");
    

    虽然这不一定能回答笼统的问题(我认为您要查找的内容不存在),但它可能会解决测试日志记录的特定问题。

    【讨论】:

      【解决方案2】:

      如果您可以从被测类外部化您的记录器对象的创建,那么您没有理由不能编写自己的日志接口测试实现来记录执行了哪些方法并将其作为您的一部分注入测试设置。

      模拟库有很多好处,但有时像您发现的一些极端情况可能无法满足您的需求。

      如果你为这样的测试编写自己的实现,并将其注入你的测试类中,那么你可以在getCount() &gt; 0上断言

      public class LoggerTestSupportImpl implements ILogger {
      
          private int count = 0;
      
          @Override
          public int getCount() {
              return count;
          }
      
          @Override
          public void info(String message) {
              count++;    
          }
      
          @Override
          public void warn(String message) {
              count++;    
          }   
      }
      

      【讨论】:

      • 这正是我迄今为止测试我的课程的方式,我只是想知道 Mockito 是否可以让我免于为所有方法编写琐碎的实现。
      • 我想说你一直在做明智的事情 Michal。 Mockito 不支持这种测试。您唯一的选择是走凯文提到的ArgumentCaptor 路线,但这会导致您的测试课程中出现很多“噪音”。
      • 同意它有点吵,但大多数噪音都局限于声明和 Before 方法,如果要跨多个测试方法使用 Captor,您甚至可以将 Captor 添加到类级别声明(不是即使不访问味精也需要)。使用 Mockito 的好处是您已经可以访问完整的日志记录 API。要在您的示例中这样做,您必须扩展手动模拟的功能(捕获/检索任意记录的消息数等)但是对于简单的需求,您的方式似乎更简单、更清洁、可重复使用,因此这些是您的方法的一些优点。
      • 很遗憾,这个解决方案显然没有任何简写符号。有谁知道为什么 Mockito API 允许测试“无交互”但不允许测试否定,即“任何交互”?如果不是使用检查条件并根据结果抛出异常的方法 (verifyZeroInteractions()),他们创建了一个返回状态的布尔方法,我可以同时编写 assertTrue(anyInteractions(mock))assertFalse(anyInteractions(mock))。我可以尝试/抓住AssertionError 并以这种方式检查我的状况,但这太疯狂了。
      • 有人可能会争辩说,单元测试应该能够确定要调用哪些方法,并且尝试测试“任何”交互不是正确的测试。您是否无法在启用了已知级别的日志记录的情况下执行您的特定场景?
      【解决方案3】:

      如果您想检查与模拟对象发生的任何交互,您可以使用Mockito.mockingDetails() 方法并检查调用次数是否不为零。当然你也可以根据模拟细节做更详细的断言,但我想只检查不为零就可以回答你的问题。

      @ExtendWith(MockitoExtension.class)
      public class TestClass {
      
          @Mock
          private Logger logger;
      
          @InjectMocks
          private Service service;
      
          @Test
          public void testMethod_shouldLogMultipleTimes() {
              service.testMethod();
              assertThat(Mockito.mockingDetails(logger).getInvocations().size()).isNotZero();
          }
      }
      

      代码示例使用assertj 来检查调用次数是否不为零。

      【讨论】:

        猜你喜欢
        • 2011-07-22
        • 1970-01-01
        • 2013-06-16
        • 2021-07-13
        • 2012-08-14
        • 2016-08-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多