如何通过 JMockit 和 Logger 接口使用动态部分模拟?

How to use dynamic partial mocking with JMockit and Logger interface?

我一直在使用 JMockit 和它公认的陡峭的学习曲线。我对一般的嘲笑还很陌生,所以请原谅我的无知。

我正在尝试模拟 Logger 接口,以便我可以验证 catch 语句是否正常工作。将此称为理解 JMockit 工作原理的练习。 Logger 接口的实现 class 是 Log4jLoggerAdapter 所以我想如果我将它的一个实例传递到我的 Expectations() 块中,JMockit 将使用动态部分模拟和 "see" 我的记录器语句。相反,我收到以下错误:

mockit.internal.MissingInvocation:缺少调用:org.slf4j.impl.Log4jLoggerAdapter#error(String msg, Throwable t)

Class正在测试

public class MyLoggedClass {
  private static final Logger LOGGER = LoggerFactory.getLogger(MyLoggedClass.class);

  ... // Other variables

  @Override
  public void connect() {
      String info = getServiceInfo();
      try {
        connector = MyConnectionFactory.connect(info);
      } catch (Exception e) {
        LOGGER.error("Exception connecting to your service with: " + info, e);
      }
  }

  ...  // Other methods
}

我的@Test

public class MyLoggedClassTest {

@Tested
MyLoggedClass myLoggedClass;

@Test
public void myLoggingTest(@Mocked final Log4jLoggerAdapter logger){

    new Expectations(MyConnectionFactory.class, logger){{
      MyConnectionFactory.connect(anyString);
      result = new Exception();

      logger.error(anyString, (Throwable)any);
    }};

    myLoggedClass.connect();
}

我会详细介绍我尝试过的其他方法,但此页面会变成一本书。这是我最好的方法。有什么想法吗?

* 更新 *(是的,很快)

我将 @Mocked 更改为 @Cascading 并从我的 Expectations 签名中删除了记录器字段并且它起作用了。我不明白为什么。有人可以提供解释吗?摸索直到你偶然发现一些有用但你不理解的东西并不是成功的秘诀。见下文:

@Test
public void myLoggingTest(@Cascading final Log4jLoggerAdapter logger){

    new Expectations(MyConnectionFactory.class){{
      MyConnectionFactory.connect(anyString);
      result = new Exception();

      logger.error(anyString, (Throwable)any);
    }};

    myLoggedClass.connect();
}

在这种情况下不需要部分模拟,只需以通常的方式模拟 MyConnectionFactory。唯一棘手的部分是如何模拟实现 Logger 接口的 class,考虑到它是从静态 class 初始化器实例化的。碰巧的是,模拟 API 中有一个功能(使用 JMockit 1.14):

public class MyLoggedClassTest
{
    @Tested MyLoggedClass myLoggedClass;

    @Test
    public void myLoggingTest(
        @Mocked MyConnectionFactory conFac, 
        @Capturing final Logger logger)
    {
        new Expectations() {{
            MyConnectionFactory.connect(anyString);
            result = new Exception();
        }};

        myLoggedClass.connect();

        new Verifications() {{
            logger.error(anyString, (Throwable)any);
        }};
    }
}

使用 @Capturing 模拟类型,任何实现 class 都会被模拟,因此测试不需要了解 Log4jLoggerAdapter.