diff --git a/README.md b/README.md index 215bb78..c8937b6 100644 --- a/README.md +++ b/README.md @@ -3,70 +3,41 @@ [](https://dmtech.de/) [![Build Status](https://travis-ci.org/dm-drogeriemarkt/log-capture.svg?branch=master)](https://travis-ci.org/dm-drogeriemarkt/log-capture) -Helper for Unit/Integration tests with JUnit 4/5 to test if something has been logged. See [Examples](#examples). +Simple assertions for log messages. See [Examples](#examples). -Because this is a library, Checkstyle is used to make sure all public classes/methods have appropriate Javadoc. +```java +logCapture + .info().assertLogged("hello world") + .warn().assertLogged("bye world"); +``` **Table of Contents** -* [Changes](#changes) - * [3.1.0](#310) - * [3.0.0](#300) - * [2.0.1](#201) - * [Updating from Version 1.x.x to 2.x.x](#updating-from-version-1xx-to-2xx) * [Usage](#usage) - * [Junit 4 vs 5](#junit-4-vs-5) * [Maven](#maven) * [Examples](#examples) * [Unit Test Example:](#unit-test-example) * [Integration Test Example:](#integration-test-example) * [Example with MDC](#example-with-mdc) -* [Usage with non-JUnit Runner](#usage-with-non-junit-runner) +* [Usage outside of JUnit 5 (Cucumber example)](#usage-outside-of-junit-5-cucumber-example) * [Cucumber example](#cucumber-example) * [Cucumber feature file](#cucumber-feature-file) * [Cucumber stepdefs](#cucumber-stepdefs) * [Cucumber DTOs](#cucumber-dtos) - - -## Changes - -### 3.1.0 - -Added `assertNothingElseLogged()` - -### 3.0.0 - -Updated from JUnit 4 to JUnit 5. - -To update from 2.x.x to 3.x.x: - -* Use JUnit 5 instead of JUnit 4 -* Replace `@Rule` in logging tests with `@RegisterExtension` - -### 2.0.1 - -Fixed a bug where multiline log messages (for example Messages that contain a stack trace) could not be matched. - -### Updating from Version 1.x.x to 2.x.x - -* `LogCapture.forUnitTest()` has been replaced with `LogCapture.forCurrentPackage()` -* `LogCapture.forIntegrationTest(...)` has been replaced with `LogCapture.forPackages(...)` -* `logCapture.addAppender()` has been replaced with `logCapture.addAppenderAndSetLogLevelToDebug()` -* `logCapture.removeAppender()` has been replaced with `logCapture.removeAppenderAndResetLogLevel()` +* [Changes](#changes) + * [3.2.0](#320) + * [3.1.0](#310) + * [3.0.0](#300) + * [2.0.1](#201) + * [Updating from Version 1.x.x to 2.x.x](#updating-from-version-1xx-to-2xx) ## Usage -### Junit 4 vs 5 - -If you still use Junit 4, you need to use LogCapture 2.x.x - -There is no guarantee, however, if and how long 2.x.x will be maintained. We plan to maintain it as long as it is needed, though. - ### Maven Add log-capture as a test dependency to your project. If you use Maven, add this to your pom.xml: -```pom.xml +```xml de.dm.infrastructure log-capture @@ -97,6 +68,13 @@ public class MyUnitTest { log.info("something interesting"); log.error("something terrible"); + //assert that the messages have been logged + //expected log message is a regular expression + logCapture + .info().assertLogged("^something interesting$") + .error().assertLogged("terrible") + + // alterative using the old, non-fluent API logCapture .assertLogged(Level.INFO, "^something interesting$") //second parameter is a regular expression .thenLogged(Level.ERROR, "terrible") @@ -131,9 +109,15 @@ public class MyIntegrationTest { log.info("something interesting"); log.error("something terrible"); + logCapture + .info().assertLogged("^something interesting$") + .info().assertLogged("^start of info from utility.that.logs") // no $ at the end to only match the start of the message + .error().("terrible"); + + // alterative using the old, non-fluent API logCapture .assertLogged(Level.INFO, "^something interesting") - .assertLogged(Level.INFO, "^info from utility.that.logs") + .assertLogged(Level.INFO, "^start of info from utility.that.logs") .thenLogged(Level.ERROR, "terrible"); } } @@ -161,7 +145,22 @@ public class MyUnitTest { MDC.put("my_mdc_key", "this is the MDC value"); MDC.put("other_mdc_key", "this is the other MDC value"); log.info("this message has some MDC information attached"); + log.info("this message has some MDC information attached, too"); + + logCapture + .info() + .withMdc("my_mdc_key", "^this is the MDC value$") + .withMdc("other_mdc_key", "^this is the other MDC value$") + .assertLogged("information attached") + + // to assert MDC content in both messages + logCapture + .withMdcForAll("my_mdc_key", "^this is the MDC value$") + .withMdcForAll("other_mdc_key", "^this is the other MDC value$") + .info().assertLogged("information attached") + .info().assertLogged("information attached, too") + // non-fluent API (no equivalent to withMdcForAll here) logCapture .assertLogged(Level.INFO, "information attached", withMdc("my_mdc_key", "^this is the MDC value$"), @@ -197,9 +196,9 @@ java.lang.AssertionError: Expected log message has occurred, but never with the other_mdc_key: "this is the other MDC value" ``` -## Usage with non-JUnit Runner +## Usage outside of JUnit 5 (Cucumber example) -If you intend to use LogCapture outside of a JUnit test, you cannot rely on JUnit's `@Rule` annotation and must call LocCapture's `addAppenderAndSetLogLevelToDebug()` and `removeAppenderAndResetLogLevel()` methods manually. +If you intend to use LogCapture outside of a JUnit test, you cannot rely on JUnit's `@Rule` annotation and must call LocCapture's `addAppenderAndSetLogLevelToTrace()` and `removeAppenderAndResetLogLevel()` methods manually. Be aware that this will still cause JUnit to be a dependency. @@ -231,7 +230,7 @@ public class LoggingStepdefs { @Before public void setupLogCapture() { - logCapture.addAppenderAndSetLogLevelToDebug(); + logCapture.addAppenderAndSetLogLevelToTrace(); } @After @@ -281,3 +280,34 @@ public class LogEntry { private String messageRegex; } ``` + +## Changes + +### 3.2.0 + +Added fluent API + +### 3.1.0 + +Added `assertNothingElseLogged()` + +### 3.0.0 + +Updated from JUnit 4 to JUnit 5. + +To update from 2.x.x to 3.x.x: + +* Use JUnit 5 instead of JUnit 4 +* Replace `@Rule` in logging tests with `@RegisterExtension` + +### 2.0.1 + +Fixed a bug where multiline log messages (for example Messages that contain a stack trace) could not be matched. + +### Updating from Version 1.x.x to 2.x.x + +* `LogCapture.forUnitTest()` has been replaced with `LogCapture.forCurrentPackage()` +* `LogCapture.forIntegrationTest(...)` has been replaced with `LogCapture.forPackages(...)` +* `logCapture.addAppender()` has been replaced with `logCapture.addAppenderAndSetLogLevelToTrace()` +* `logCapture.removeAppender()` has been replaced with `logCapture.removeAppenderAndResetLogLevel()` + diff --git a/src/main/java/de/dm/infrastructure/logcapture/FluentLogAssertion.java b/src/main/java/de/dm/infrastructure/logcapture/FluentLogAssertion.java new file mode 100644 index 0000000..7693c54 --- /dev/null +++ b/src/main/java/de/dm/infrastructure/logcapture/FluentLogAssertion.java @@ -0,0 +1,141 @@ +package de.dm.infrastructure.logcapture; + +import ch.qos.logback.classic.Level; +import de.dm.infrastructure.logcapture.LogCapture.LastCapturedLogEvent; +import lombok.RequiredArgsConstructor; + +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +import static ch.qos.logback.classic.Level.DEBUG; +import static ch.qos.logback.classic.Level.ERROR; +import static ch.qos.logback.classic.Level.INFO; +import static ch.qos.logback.classic.Level.TRACE; +import static ch.qos.logback.classic.Level.WARN; +import static lombok.AccessLevel.PACKAGE; +import static lombok.AccessLevel.PRIVATE; + +/** + * Helper class for fluent log assertions. Use this via {@link LogCapture#info()} et al. or {@link LogCapture#withMdcForAll(String, String)} + */ +@RequiredArgsConstructor(access = PACKAGE) +public class FluentLogAssertion { + private final LogCapture logCapture; + private final Optional lastCapturedLogEvent; + private final List expectedMdcEntries = new LinkedList<>(); + + /** + * prepare the assertion of log messages with MDC contents + * + * @param key MDC key + * @param regex regular expression describing the MDC value + * + * @return FluentLogAssertion to assert the messages with MDC + */ + public FluentLogAssertion withMdcForAll(String key, String regex) { + FluentLogAssertion newAssertion = new FluentLogAssertion(logCapture, lastCapturedLogEvent); + newAssertion.expectedMdcEntries.addAll(expectedMdcEntries); + newAssertion.expectedMdcEntries.add(ExpectedMdcEntry.withMdc(key, regex)); + + return newAssertion; + } + + /** + * prepare the assertion of a logged warn message + * + * @return FluentLogAssertion to assert an warn message + */ + public ConfiguredLogAssertion warn() { + return new ConfiguredLogAssertion(WARN); + } + + /** + * prepare the assertion of a logged error message + * + * @return FluentLogAssertion to assert an error message + */ + public ConfiguredLogAssertion error() { + return new ConfiguredLogAssertion(ERROR); + } + + /** + * prepare the assertion of a logged info message + * + * @return FluentLogAssertion to assert an info message + */ + public ConfiguredLogAssertion info() { + return new ConfiguredLogAssertion(INFO); + } + + /** + * prepare the assertion of a logged debug message + * + * @return FluentLogAssertion to assert an debug message + */ + public ConfiguredLogAssertion debug() { + return new ConfiguredLogAssertion(DEBUG); + } + + /** + * prepare the assertion of a logged trace message + * + * @return FluentLogAssertion to assert an trace message + */ + public ConfiguredLogAssertion trace() { + return new ConfiguredLogAssertion(TRACE); + } + + /** + * assert that nothing else has been logged + */ + public void assertNothingElseLogged() { + LastCapturedLogEvent presentEvent = lastCapturedLogEvent.orElseThrow(() -> + new IllegalStateException("assertNothingElseLogged() must be called with a previous log assertion")); + presentEvent.assertNothingElseLogged(); + } + + /** + * Helper class for fluent log assertions. Use this via {@link LogCapture#info()} et al. or {@link LogCapture#withMdcForAll(String, String)} + */ + @RequiredArgsConstructor(access = PRIVATE) + public class ConfiguredLogAssertion { + private final Level level; + private final List expectedMdcEntries = new LinkedList<>(FluentLogAssertion.this.expectedMdcEntries); + + /** + * configure the next message assertion to also assert MDC contents + * + * @param key MDC key + * @param regex regular expression describing the MDC value + * + * @return pre-configured assertion + */ + public ConfiguredLogAssertion withMdc(String key, String regex) { + ConfiguredLogAssertion newAssertion = new ConfiguredLogAssertion(level); + newAssertion.expectedMdcEntries.addAll(expectedMdcEntries); + newAssertion.expectedMdcEntries.add(ExpectedMdcEntry.withMdc(key, regex)); + + return newAssertion; + } + + /** + * assert that a message has been logged, depending on previous configuration + * + * @param regex regex to match formatted log message (with Pattern.DOTALL and Pattern.MULTILINE) + * + * @return configuration to allow further assertions + */ + public FluentLogAssertion assertLogged(String regex) { + final LastCapturedLogEvent capturedLogEvent; + if (lastCapturedLogEvent.isPresent()) { + capturedLogEvent = lastCapturedLogEvent.get().thenLogged(level, regex, expectedMdcEntries.toArray(new ExpectedMdcEntry[0])); + } else { + capturedLogEvent = logCapture.assertLogged(level, regex, expectedMdcEntries.toArray(new ExpectedMdcEntry[0])); + } + FluentLogAssertion newFluentLogAssertion = new FluentLogAssertion(logCapture, Optional.of(capturedLogEvent)); + newFluentLogAssertion.expectedMdcEntries.addAll(FluentLogAssertion.this.expectedMdcEntries); + return newFluentLogAssertion; + } + } +} diff --git a/src/main/java/de/dm/infrastructure/logcapture/LogCapture.java b/src/main/java/de/dm/infrastructure/logcapture/LogCapture.java index b4c89c0..ca700bd 100644 --- a/src/main/java/de/dm/infrastructure/logcapture/LogCapture.java +++ b/src/main/java/de/dm/infrastructure/logcapture/LogCapture.java @@ -11,6 +11,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.Optional; import java.util.Set; import static org.slf4j.Logger.ROOT_LOGGER_NAME; @@ -62,7 +63,7 @@ private LogCapture(Set capturedPackages) { @Override public void beforeEach(ExtensionContext context) { - addAppenderAndSetLogLevelToDebug(); + addAppenderAndSetLogLevelToTrace(); } @Override @@ -76,28 +77,28 @@ public void afterEach(ExtensionContext context) { * For example, this may be used in a Method that is annotated with Cucumber's @Before annotation to start capturing. * In this case, make sure you also call {@link LogCapture#removeAppenderAndResetLogLevel()} in an @After method */ - public void addAppenderAndSetLogLevelToDebug() { + public void addAppenderAndSetLogLevelToTrace() { capturingAppender = new CapturingAppender(rootLogger.getLoggerContext(), capturedPackages); rootLogger.addAppender(capturingAppender); - setLogLevelToDebug(); + setLogLevelToTrace(); } - private void setLogLevelToDebug() { + private void setLogLevelToTrace() { if (originalLogLevels != null) { - throw new IllegalStateException("LogCapture.addAppenderAndSetLogLevelToDebug() should not be called only once or after calling removeAppenderAndResetLogLevel() again."); + throw new IllegalStateException("LogCapture.addAppenderAndSetLogLevelToTrace() should not be called only once or after calling removeAppenderAndResetLogLevel() again."); } originalLogLevels = new HashMap<>(); capturedPackages.forEach(packageName -> { Logger packageLogger = rootLogger.getLoggerContext().getLogger(packageName); originalLogLevels.put(packageName, packageLogger.getLevel()); - rootLogger.getLoggerContext().getLogger(packageName).setLevel(Level.DEBUG); + rootLogger.getLoggerContext().getLogger(packageName).setLevel(Level.TRACE); } ); } private void resetLogLevel() { if (originalLogLevels == null) { - throw new IllegalStateException("LogCapture.resetLogLevel() should only be called after calling addAppenderAndSetLogLevelToDebug()"); + throw new IllegalStateException("LogCapture.resetLogLevel() should only be called after calling addAppenderAndSetLogLevelToTrace()"); } capturedPackages.forEach(packageName -> rootLogger.getLoggerContext().getLogger(packageName).setLevel(originalLogLevels.get(packageName)) @@ -133,7 +134,7 @@ public LastCapturedLogEvent assertLogged(Level level, String regex, ExpectedMdcE private LastCapturedLogEvent assertLogged(Level level, String regex, LastCapturedLogEvent lastCapturedLogEvent, ExpectedMdcEntry... expectedMdcEntries) { if (capturingAppender == null) { throw new IllegalStateException("capuringAppender is null. " + - "Please make sure that either LogCapture is used with a @Rule annotation or that addAppenderAndSetLogLevelToDebug is called manually."); + "Please make sure that either LogCapture is used with a @Rule annotation or that addAppenderAndSetLogLevelToTrace is called manually."); } Integer startIndex = lastCapturedLogEvent == null ? 0 : lastCapturedLogEvent.index + 1; @@ -179,4 +180,104 @@ public void assertNothingElseLogged() { } } + /** + * prepare the assertion of log messages with MDC contents + * + * @param key MDC key + * @param regex regular expression describing the MDC value + * + *

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