#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();
}
}