【问题标题】:How to mock static method in JUnit如何在 JUnit 中模拟静态方法
【发布时间】:2021-08-18 06:30:32
【问题描述】:

在带有 slf4j 和 logback 的 spring-boot 环境中的单元测试中,我想确保 LOGGER.isTraceEnabled() 仅针对特定测试类返回 true。

这样做的原因是,有时我们使用 if (LOGGER.isTraceEnabled()) {...} 保护缓慢且非平凡的代码。在单元测试中,我们希望确保在打开跟踪时它不会破坏应用程序。

被测代码:

public class ClassUnderTest{

    private static final Logger LOGGER = LoggerFactory.getLogger(ClassUnderTest.class);

    public void doSomething(Calendar gegenwart) {
        if (LOGGER.isTraceEnabled()) {
           // non trivial code
        }
    }
}

测试代码:

public class ClassUnderTestUnitTest{

    @Test
    public void findeSnapshotMitLueke() {
        ClassUnderTest toTestInstance = new ClassUnderTest ();

        // I want to make sure trace logging is enabled, when
        // this method is executed.
        // Trace logging should not be enabled outside this
        // test class.
        toTestInstance.doSomething(Calendar.getInstance());
    }

}

【问题讨论】:

  • 您到底想测试什么?当启用跟踪或仅针对此特定类启用跟踪时,非平凡代码是否有效?
  • 非平凡的代码有效。

标签: java spring-boot junit logback slf4j


【解决方案1】:

您可以使用 PowerMock 轻松创建 LoggerFactory 的静态模拟,并使其返回 Logger 的常规模拟(使用 EasyMock)。然后,您只需定义 Logger.isTraceEnabled()Logger.trace() 的模拟实现。

在我的头顶:


@PrepareForTest({ LoggerFactory.class })
@RunWith(PowerMockRunner.class) // assuming JUnit...
public class ClassUnderTestUnitTest{

    @Test
    public void findeSnapshotMitLueke() {

        Logger mockLogger = EasyMock.createMock(Logger.class);
        EasyMock.expect(mockLogger.isTraceEnabled()).andReturn(true);
        EasyMock.expect(mockLogger.trace(any()));
        EasyMock.expectLastCall().anyTimes() // as trace is a void method.
        // Repeat for other log methods ...

        PowerMock.mockStatic(LoggerFactory.class);
        EasyMock.expect(LoggerFactory.getLogger(ClassUnderTest.class)
               .andReturn(mockLogger);

        PowerMock.replay(mockLogger, LoggerFactory.class);

        ClassUnderTest toTestInstance = new ClassUnderTest ();

        // I want to make sure trace logging is enabled, when
        // this method is executed.
        // Trace logging should not be enabled outside this
        // test class.
        toTestInstance.doSomething(Calendar.getInstance());

        // After the operation if needed you can verify that the mocked methods were called.
        
        PowerMock.verify(mockLogger).times(...);
        PowerMock.verifyStatic(LoggerFactory.class).times(...);
    }

}

【讨论】:

  • 不错的解决方案,如果您准备引入 2 个新框架。猜猜它也适用于 PowerMockito。
  • PowerMockito 实际上是 PowerMock 的一部分。 PowerMockito 之于 Mockito 就像 PowerMock 之于 EasyMock - 在这两种情况下,您都使用两个框架 - 一个用于常规模拟,一种用于静态和最终模拟的 PowerMock。但底线是您需要使用 PowerMock 的风格来实现 Chris311 所追求的结果。
  • 附带说明 - 虽然根据我的经验,Mockito 更易于使用,但它为误报创造了太多机会,因为它默认创建了很好的模拟。 EasyMock 明确说明创建的模拟类型,并且不会在返回 null 的非模拟方法后面隐藏错误。
【解决方案2】:

如果您不想使用像 powermock 这样的框架,您可以执行以下技巧:

public class ClassUnderTest {

    private Supplier<Logger> loggerSupplier = () -> getLogger(ClassUnderTest.class);

    public void doSomething(Calendar gegenwart) {
        if (loggerSupplier.get().isTraceEnabled()) {
           // non trivial code
        }
    }
}

现在您可以模拟记录器了:

public class ClassUnderTestUnitTest{

    @Mock
    private Supplier<Mock> loggerSupplier;
    @Mock
    private Logger logger;

    @Test
    public void findeSnapshotMitLueke() {
        ClassUnderTest toTestInstance = new ClassUnderTest ();

        when(loggerSupplier.get()).thenReturn(logger);
        when(logger.isTraceEnabled()).thenReturn(true)

        toTestInstance.doSomething(Calendar.getInstance());

        // verifyLogger();
    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-23
    相关资源
    最近更新 更多