Mar 01, 2026
Table of contents
- What is InformativeAssertionLog?
- The problem it solves
- Code walkthrough
- When to use it — and when not to
- Real-world usage examples
- Setup and dependencies
1. What is InformativeAssertionLog?
InformativeAssertionLog is a thin utility class that wraps JUnit 5's Assertions and Hamcrest's MatcherAssert with structured SLF4J logging. Every assertion it makes is automatically logged — including the human-readable message, the expected value, and the actual value — before the underlying assertion is evaluated.
The result: test logs that read like a specification, not a wall of cryptic failure messages.
Core idea: Instead of reading "AssertionError: expected true but was false", your CI logs now say "Assert that HTTP response status is 200 — expected: 200 — actual: 404". No more guessing what went wrong.
2. The problem it solves
Standard JUnit 5 and Hamcrest assertions are excellent at catching failures, but their log output at runtime is minimal. When a test suite with hundreds of assertions fails in CI, you're often left with a bare stack trace pointing to a line number. You must open the source code, find the assertion, and mentally reconstruct what was being checked.
This becomes especially painful in three scenarios:
- Regression suites. Long suites that run overnight. Log context is the only debugging tool available until morning.
- Cross-team handoffs. When QA hands a failure report to devs, "expected X, got Y" is far more actionable than a line number.
3. Code walkthrough
Here is the full class, with annotations on each method:
import org.apache.commons.lang3.StringUtils;
import org.hamcrest.Matcher;
import org.hamcrest.MatcherAssert;
import org.junit.jupiter.api.Assertions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
public class InformativeAssertionLog {
private static final String MESSAGE_EXPECTED_ACTUAL = " {}{} - expected: {}{} - actual: {}";
private static Logger logger = LoggerFactory.getLogger(InformativeAssertionLog.class);
public static void assertTrue(String message, boolean condition) {
String fullMessage = "Assert that " + message;
logger.info(MESSAGE_EXPECTED_ACTUAL, fullMessage, System.lineSeparator(), Boolean.TRUE, System.lineSeparator(), condition);
Assertions.assertTrue(condition, fullMessage);
}
public static void assertFalse(String message, boolean condition) {
String fullMessage = "Assert that " + message;
logger.info(MESSAGE_EXPECTED_ACTUAL, fullMessage, System.lineSeparator(), Boolean.FALSE, System.lineSeparator(), condition);
Assertions.assertFalse(condition,fullMessage);
}
public static <T> void assertThat(String message, T actual, Matcher<? super T> matcher) {
assertThat(message, "", actual, matcher);
}
public static <T> void assertThat(String logMessage, String failureMessage, T actual, Matcher<? super T> matcher) {
String fullMessage = "Assert that " + logMessage;
logger.info(MESSAGE_EXPECTED_ACTUAL, fullMessage, System.lineSeparator(), matcher.toString(), System.lineSeparator(), actual);
if (StringUtils.isEmpty(failureMessage)) {
MatcherAssert.assertThat(actual, matcher);
} else {
MatcherAssert.assertThat(failureMessage, actual, matcher);
}
}
public static <T> void assertThat(String message, List<T> param, String failureMessage, T actual, Matcher<? super T> matcher) {
logger.info(message, param);
assertThat(message, failureMessage, actual, matcher);
}
}
4. When to use it — and when not to
Use InformativeAssertionLog when:
- Your test suite runs in a CI pipeline where live debugging is not possible
- You're performing API contract or integration tests with many field-level assertions
- QA and developers work from the same log artifacts — shared language matters
- Regression test failures need to be triaged quickly, often by people who didn't write the test
- You're building a reusable assertion library across multiple test projects in a monorepo
Consider alternatives when:
- You only have a handful of unit tests where JUnit's default output is sufficient
- Your test framework already has a structured reporting layer (e.g. Allure, ExtentReports)
- Log verbosity is constrained — every assertion produces an INFO log line
5. Real-world usage examples
API response validation
@Test
void userProfileReturnsExpectedFields() {
Response response = userApi.getProfile("user-42");
// Logs: "Assert that response status is 200 - expected: <200> - actual: 200"
InformativeAssertionLog.assertThat(
"response status is 200",
response.getStatusCode(),
equalTo(200)
);
// Logs: "Assert that username field is not null or empty - expected: not null ..."
InformativeAssertionLog.assertThat(
"username field is not null or empty",
response.jsonPath().getString("username"),
notNullValue()
);
}
Parameterised test data logging
@ParameterizedTest
@MethodSource("provideUserRoles")
void eachRoleHasCorrectPermissions(String role, List<String> expectedPerms) {
// Lists the expected perms in the log line for this iteration
InformativeAssertionLog.assertThat(
"role {} should have permissions: {}",
List.of(role, expectedPerms),
"permission list mismatch for role: " + role,
permissionService.getPermissions(role),
containsInAnyOrder(expectedPerms.toArray())
);
}
6. Setup and dependencies
Add the following to your pom.xml. All of these are likely already present in a typical Spring Boot or REST Assured project:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.2</version> <!-- Using a recent stable version -->
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.17</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.25.4</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.20.0</version>
</dependency>