Example: + *
{@code + * logCapture + * .withMdcForAll("key", "value") + * .info().assertLogged("hello world") + * .warn().assertLogged("bye world")); + * }+ * + * @return FluentLogAssertion to assert the messages with MDC + */ + public FluentLogAssertion withMdcForAll(String key, String regex) { + return new FluentLogAssertion(this, Optional.empty()) + .withMdcForAll(key, regex); + } + + /** + * prepare the assertion of a logged error message + * + *
Example: + *
{@code + * logCapture + * .error().assertLogged("hello world") + * }+ * + * @return FluentLogAssertion to assert an error message + */ + public FluentLogAssertion.ConfiguredLogAssertion error() { + return new FluentLogAssertion(this, Optional.empty()) + .error(); + } + + /** + * prepare the assertion of a logged warn message + * + *
Example: + *
{@code + * logCapture + * .warn().assertLogged("hello world") + * }+ * + * @return FluentLogAssertion to assert an warn message + */ + public FluentLogAssertion.ConfiguredLogAssertion warn() { + return new FluentLogAssertion(this, Optional.empty()) + .warn(); + } + + /** + * prepare the assertion of a logged info message + * + *
Example: + *
{@code + * logCapture + * .info().assertLogged("hello world") + * }+ * + * @return FluentLogAssertion to assert an info message + */ + public FluentLogAssertion.ConfiguredLogAssertion info() { + return new FluentLogAssertion(this, Optional.empty()) + .info(); + } + + /** + * prepare the assertion of a logged debug message + * + *
Example: + *
{@code + * logCapture + * .debug().assertLogged("hello world") + * }+ * + * @return FluentLogAssertion to assert an debug message + */ + public FluentLogAssertion.ConfiguredLogAssertion debug() { + return new FluentLogAssertion(this, Optional.empty()) + .debug(); + } + + /** + * prepare the assertion of a logged trace message + * + *
Example: + *
{@code + * logCapture + * .trace().assertLogged("hello world") + * }+ * + * @return FluentLogAssertion to assert an trace message + */ + public FluentLogAssertion.ConfiguredLogAssertion trace() { + return new FluentLogAssertion(this, Optional.empty()) + .trace(); + } } diff --git a/src/test/java/de/dm/infrastructure/logcapture/FluentApiTest.java b/src/test/java/de/dm/infrastructure/logcapture/FluentApiTest.java new file mode 100644 index 0000000..d10df05 --- /dev/null +++ b/src/test/java/de/dm/infrastructure/logcapture/FluentApiTest.java @@ -0,0 +1,211 @@ +package de.dm.infrastructure.logcapture; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.MDC; + +import java.util.Optional; + +import static java.lang.System.lineSeparator; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@Slf4j +class FluentApiTest { + @RegisterExtension + LogCapture logCapture = LogCapture.forCurrentPackage(); + + + @Test + void basicFluentApi() { + log.info("hello world"); + + logCapture.info().assertLogged("hello world"); + } + + @Test + void inOrderSucceeds() { + log.info("hello world"); + log.warn("bye world"); + + logCapture + .info().assertLogged("hello world") + .warn().assertLogged("bye world"); + } + + @Test + void inOrderFails() { + log.warn("bye world"); + log.info("hello world"); + + AssertionError assertionError = assertThrows(AssertionError.class, () -> + logCapture + .info().assertLogged("hello world") + .warn().assertLogged("bye world")); + + assertThat(assertionError).hasMessage("Expected log message has not occurred: Level: WARN, Regex: \"bye world\""); + } + + @Test + void nothingElseLoggedSucceeds() { + log.info("hello world"); + + logCapture + .info().assertLogged("hello world") + .assertNothingElseLogged(); + } + + @Test + void nothingElseLoggedFails() { + log.info("hello world"); + log.info("hello universe"); + + + AssertionError assertionError = assertThrows(AssertionError.class, () -> + logCapture + .info().assertLogged("hello world") + .assertNothingElseLogged()); + + assertThat(assertionError).hasMessage("There have been other log messages than the asserted ones."); + } + + @Test + void wrongUsageThrowsIllegalState() { + // this constructor is package private, so it should be effectively impossible to actually cause this IllegalStateException + FluentLogAssertion fluentLogAssertion = new FluentLogAssertion(null, Optional.empty()); + + IllegalStateException illegalStateException = assertThrows(IllegalStateException.class, () -> fluentLogAssertion.assertNothingElseLogged()); + + assertThat(illegalStateException).hasMessage("assertNothingElseLogged() must be called with a previous log assertion"); + } + + @Test + void singleMdcSucceeds() { + log.info("hello world"); + MDC.put("key", "value"); + log.warn("bye world"); + MDC.clear(); + + logCapture + .info().assertLogged("hello world") + .warn().withMdc("key", "value").assertLogged("bye world"); + } + + @Test + void singleMdcFails() { + MDC.put("key", "value"); + log.info("hello world"); + MDC.clear(); + log.warn("bye world"); + + AssertionError assertionError = assertThrows(AssertionError.class, () -> + logCapture + .info().assertLogged("hello world") + .warn().withMdc("key", "value").assertLogged("bye world")); + + assertThat(assertionError).hasMessage("Expected log message has occurred, but never with the expected MDC value: Level: WARN, Regex: \"bye world\"" + + lineSeparator() + " Captured message: \"bye world\"" + + lineSeparator() + " Captured MDC values:"); + } + + @Test + void mdcForAllFails() { + MDC.put("key", "value"); + log.info("hello world"); + MDC.clear(); + log.warn("bye world"); + + AssertionError assertionError = assertThrows(AssertionError.class, () -> + logCapture + .withMdcForAll("key", "value") + .info().assertLogged("hello world") + .warn().assertLogged("bye world")); + + assertThat(assertionError).hasMessage("Expected log message has occurred, but never with the expected MDC value: Level: WARN, Regex: \"bye world\"" + + lineSeparator() + " Captured message: \"bye world\"" + + lineSeparator() + " Captured MDC values:"); + } + + @Test + void mdcForAllSucceeds() { + MDC.put("key", "value"); + log.info("hello world"); + log.warn("bye world"); + MDC.clear(); + + logCapture + .withMdcForAll("key", "value") + .info().assertLogged("hello world") + .warn().assertLogged("bye world"); + } + + @Test + void mdcForAllAndSingleMdcFails() { + MDC.put("key", "value"); + MDC.put("another_key", "another_value"); + log.info("hello world"); + MDC.remove("another_key"); + log.warn("bye world"); + MDC.clear(); + + AssertionError assertionError = assertThrows(AssertionError.class, () -> { + logCapture + .withMdcForAll("key", "value") + .info().assertLogged("hello world") + .warn().withMdc("another_key", "another_value").assertLogged("bye world"); + }); + + assertThat(assertionError).hasMessage("Expected log message has occurred, but never with the expected MDC value: Level: WARN, Regex: \"bye world\"" + + lineSeparator() + " Captured message: \"bye world\"" + + lineSeparator() + " Captured MDC values:" + + lineSeparator() + " key: \"value\""); + } + + @Test + void mdcForAllAndSingleMdcSucceeds() { + MDC.put("key", "value"); + log.info("hello world"); + MDC.put("another_key", "another_value"); + log.warn("bye world"); + MDC.remove("another_key"); + log.error("hello again"); + MDC.clear(); + + logCapture + .withMdcForAll("key", "value") + .info().assertLogged("hello world") + .warn().withMdc("another_key", "another_value").assertLogged("bye world") + .error().assertLogged("hello again"); + } + + @Test + void allLevelsWork() { + log.error("first error"); + log.error("second error"); + log.warn("first warn"); + log.warn("second warn"); + log.info("first info"); + log.info("second info"); + log.debug("first debug"); + log.debug("second debug"); + log.trace("first trace"); + log.trace("second trace"); + + logCapture + .error().assertLogged("first error") + .error().assertLogged("second error"); + logCapture + .warn().assertLogged("first warn") + .warn().assertLogged("second warn"); + logCapture + .info().assertLogged("first info") + .info().assertLogged("second info"); + logCapture + .debug().assertLogged("first debug") + .debug().assertLogged("second debug"); + logCapture + .trace().assertLogged("first trace") + .trace().assertLogged("second trace"); + } +} diff --git a/src/test/java/de/dm/infrastructure/logcapture/LogCaptureTest.java b/src/test/java/de/dm/infrastructure/logcapture/LogCaptureTest.java index 7802b28..b5f34bf 100644 --- a/src/test/java/de/dm/infrastructure/logcapture/LogCaptureTest.java +++ b/src/test/java/de/dm/infrastructure/logcapture/LogCaptureTest.java @@ -12,6 +12,7 @@ import static ch.qos.logback.classic.Level.DEBUG; import static ch.qos.logback.classic.Level.INFO; +import static ch.qos.logback.classic.Level.TRACE; import static de.dm.infrastructure.logcapture.ExpectedMdcEntry.withMdc; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -107,17 +108,9 @@ void twoLogMessagesOutOfOrder() { @Test void catchMissingLogMessage() { - boolean assertionErrorThrown = false; - try { - logCapture.assertLogged(Level.INFO, "something that has not been logged"); - } catch (AssertionError e) { - String expectedMessage = "Expected log message has not occurred: Level: INFO, Regex: \"something that has not been logged\""; - assertThat(e.getMessage()).isEqualTo(expectedMessage); - assertionErrorThrown = true; - } - if (!assertionErrorThrown) { - throw new AssertionError("Assertion Error has not been thrown for missing log message."); - } + AssertionError assertionError = assertThrows(AssertionError.class, () -> logCapture.assertLogged(INFO, "something that has not been logged")); + + assertThat(assertionError).hasMessage("Expected log message has not occurred: Level: INFO, Regex: \"something that has not been logged\""); } @Test @@ -153,22 +146,13 @@ void correctLogMessagesWithMissingMdc() { MDC.put(MDC_KEY, actualMdcValue); log.info("some message"); - boolean assertionErrorThrown = false; - try { - logCapture - .assertLogged(Level.INFO, "some message", withMdc(MDC_KEY, "mdc value")); - } catch (AssertionError e) { - String expectedMessage = "Expected log message has occurred, but never with the expected MDC value: Level: INFO, Regex: \"some message\"" - + System.lineSeparator() + " Captured message: \"some message\"" - + System.lineSeparator() + " Captured MDC values:" - + System.lineSeparator() + " " + MDC_KEY + ": \"" + actualMdcValue + "\""; - - assertThat(e.getMessage()).isEqualTo(expectedMessage); - assertionErrorThrown = true; - } - if (!assertionErrorThrown) { - throw new AssertionError("Assertion Error has not been thrown for missing log message."); - } + AssertionError assertionError = assertThrows(AssertionError.class, () -> + logCapture.assertLogged(INFO, "some message", withMdc(MDC_KEY, "mdc value"))); + + assertThat(assertionError).hasMessage("Expected log message has occurred, but never with the expected MDC value: Level: INFO, Regex: \"some message\"" + + System.lineSeparator() + " Captured message: \"some message\"" + + System.lineSeparator() + " Captured MDC values:" + + System.lineSeparator() + " " + MDC_KEY + ": \"" + actualMdcValue + "\""); } @Test @@ -194,9 +178,9 @@ private void logLevelIsResetTo(Level originalLevel) { comExampleLogger.setLevel(originalLevel); LogCapture logCapture = LogCapture.forPackages("com.example"); - logCapture.addAppenderAndSetLogLevelToDebug(); + logCapture.addAppenderAndSetLogLevelToTrace(); - assertThat(comExampleLogger.getLevel()).isEqualTo(DEBUG); + assertThat(comExampleLogger.getLevel()).isEqualTo(TRACE); logCapture.removeAppenderAndResetLogLevel(); diff --git a/src/test/java/de/dm/infrastructure/logcapture/LogCaptureWrongUsageTest.java b/src/test/java/de/dm/infrastructure/logcapture/LogCaptureWrongUsageTest.java index 6370671..42faa69 100644 --- a/src/test/java/de/dm/infrastructure/logcapture/LogCaptureWrongUsageTest.java +++ b/src/test/java/de/dm/infrastructure/logcapture/LogCaptureWrongUsageTest.java @@ -10,9 +10,9 @@ class LogCaptureWrongUsageTest { @Test void doubleInitializationFails() { - logCapture.addAppenderAndSetLogLevelToDebug(); + logCapture.addAppenderAndSetLogLevelToTrace(); - assertThrows(IllegalStateException.class, () -> logCapture.addAppenderAndSetLogLevelToDebug()); + assertThrows(IllegalStateException.class, () -> logCapture.addAppenderAndSetLogLevelToTrace()); } @Test