Skip to content

Instantly share code, notes, and snippets.

@amirmv2006
Created June 11, 2020 18:12
Show Gist options
  • Save amirmv2006/27a7b385aeba1b25c2f59def7936c846 to your computer and use it in GitHub Desktop.
Save amirmv2006/27a7b385aeba1b25c2f59def7936c846 to your computer and use it in GitHub Desktop.
Mock Logging in JUnit 5
package ir.amv.os.test.helper;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.Appender;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.slf4j.LoggerFactory;
import org.springframework.core.ResolvableType;
/**
* @author Amir
*/
public class LogMockingExtension implements BeforeEachCallback, AfterEachCallback, ParameterResolver {
private Appender<ILoggingEvent> mockedAppender;
private ArgumentCaptor<LoggingEvent> eventCaptor;
private Supplier<LoggingEvent> singleLogEventSupplier;
private Supplier<Stream<LoggingEvent>> streamLogEventSupplier;
@Override
public boolean supportsParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
Class<?> paramType = parameterContext.getParameter().getType();
Class<?> genericType = Optional.of(parameterContext.getParameter().getParameterizedType())
.map(ResolvableType::forType)
.map(rt -> rt.getGeneric(0))
.map(ResolvableType::getRawClass)
.orElse(null);
Class<?> genericGenericType = Optional.of(parameterContext.getParameter().getParameterizedType())
.map(ResolvableType::forType)
.map(rt -> rt.getGeneric(0))
.map(rt -> rt.getGeneric(0))
.map(ResolvableType::getRawClass)
.orElse(null);
return paramType.isAssignableFrom(Appender.class) ||
paramType.isAssignableFrom(ArgumentCaptor.class) ||
(paramType.isAssignableFrom(Supplier.class) && genericType.isAssignableFrom(LoggingEvent.class)) ||
(paramType.isAssignableFrom(Supplier.class) && genericType.isAssignableFrom(Stream.class) && genericGenericType.isAssignableFrom(LoggingEvent.class))
;
}
@Override
public Object resolveParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
Class<?> paramType = parameterContext.getParameter().getType();
Class<?> genericType = Optional.of(parameterContext.getParameter().getParameterizedType())
.map(ResolvableType::forType)
.map(rt -> rt.getGeneric(0))
.map(ResolvableType::getRawClass)
.orElse(null);
Class<?> genericGenericType = Optional.of(parameterContext.getParameter().getParameterizedType())
.map(ResolvableType::forType)
.map(rt -> rt.getGeneric(0))
.map(rt -> rt.getGeneric(0))
.map(ResolvableType::getRawClass)
.orElse(null);
if (paramType.isAssignableFrom(Appender.class)) {
return mockedAppender;
} else if (paramType.isAssignableFrom(ArgumentCaptor.class)) {
return eventCaptor;
} else if (paramType.isAssignableFrom(Supplier.class) && genericType.isAssignableFrom(LoggingEvent.class)) {
return singleLogEventSupplier;
} else if (paramType.isAssignableFrom(Supplier.class) && genericType.isAssignableFrom(Stream.class) && genericGenericType.isAssignableFrom(LoggingEvent.class)) {
return streamLogEventSupplier;
}
return null;
}
@Override
public void beforeEach(ExtensionContext context) throws Exception {
mockedAppender = Mockito.mock(Appender.class);
((ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME))
.addAppender(mockedAppender);
eventCaptor = ArgumentCaptor.forClass(LoggingEvent.class);
singleLogEventSupplier = () -> {
Mockito.verify(mockedAppender).doAppend(eventCaptor.capture());
return eventCaptor.getValue();
};
streamLogEventSupplier = () -> {
Mockito.verify(mockedAppender, Mockito.atLeastOnce()).doAppend(eventCaptor.capture());
return eventCaptor.getAllValues().stream();
};
}
@Override
public void afterEach(ExtensionContext context) throws Exception {
mockedAppender = null;
eventCaptor = null;
singleLogEventSupplier = null;
streamLogEventSupplier = null;
}
}
@amirmv2006
Copy link
Author

Usage:

@ExtendWith(LogMockingExtension.class)
public class ExampleLogMockingUsage {

  @Test
  void testSingleLogSupplier(Supplier<LoggingEvent> logSupplier) {
    // given
    // when
    // then
    LoggingEvent loggingEvent = logSupplier.get();
    assertEquals(Level.WARN, loggingEvent.getLevel());
  }

  @Test
  void testMultipleLogsSupplier(Supplier<Stream<LoggingEvent>> logStreamSupplier) {
    // given
    // when
    // then
    assertTrue(logStreamSupplier.get()
        .anyMatch(loggingEvent -> loggingEvent.getLevel() == Level.WARN));
  }

  @ParameterizedTest
  @...Source
  void logParamsShouldBeLast(Long x, int y, String w, Supplier<LoggingEvent> logSupplier) {
    // given
    // when
    // then
    LoggingEvent loggingEvent = logSupplier.get();
    assertEquals(Level.WARN, loggingEvent.getLevel());
  }

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment