如何在 JUnit 中模拟静态方法

How to mock static method in JUnit

在带有 slf4j 和 logback 的 spring-boot 环境中的单元测试中,我想确保 LOGGER.isTraceEnabled() returns 仅对特定测试 class 为真.

原因是,有时我们会用 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());
    }

}

您可以使用 PowerMock 轻松创建 LoggerFactory 的静态模拟,并使其 return 成为 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(...);
    }

}

如果您不想使用像 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();
    }

}