Как имитировать статический метод в JUnit

#java #spring-boot #junit #logback #slf4j

Вопрос:

В модульном тесте в среде весенней загрузки с slf4j и обратной связью я хочу убедиться 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());
    }

}
 

Комментарии:

1. Что именно вы хотите проверить? Что нетривиальный код работает, когда включена трассировка, или что трассировка включена только для этого конкретного класса?

2. Что нетривиальный код работает.

Ответ №1:

Вы можете легко создать статический макет LoggerFactory с помощью PowerMock и заставить его возвращать обычный макет 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(...);
    }

}
 

Комментарии:

1. Хорошее решение, если вы готовы представить 2 новых фреймворка. Думаю, это также сработало бы с PowerMockito.

2. PowerMockito на самом деле является частью PowerMock. PowerMockito — это издевательство над тем, что такое PowerMock для EasyMock — в обоих случаях вы используете две платформы-одну для регулярного издевательства и одну разновидность PowerMock для статического и окончательного издевательства. Но суть в том, что вам нужно использовать аромат PowerMock, чтобы достичь результата, которого добивается Chris311.

3. Кстати, хотя 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();
    }

}