Logcaptor error logs do not contain expected content when reusing instance
See original GitHub issueDescribe the bug I use logcaptor in a parameterized test method of a @SpringBootIntegrationTest class to check whether the expected error message was actually logged. I loosely followed the reuse example from the readme (see code snippets below).
For every parameterization, one error message is logged in the sut (verified that via debugger). When looking at the logcaptor.getErrorLogs though, this is not always correctly reflected there.
I tried out some things and it appears as if the following things make a difference:
- Just running the test method vs. running the whole test class
- Reusing the logcaptor instance or creating a new one every time
- Having the logcaptor field as an instance variable vs. making it static
The possible results for the assertion whether the specific error message has been logged are:
- Everything ok
- Assertion fails only for the first parameterized method call
- Assertion fails for all parameterizations
static field, reuse | non-static field, reuse | static field, no reuse | non-static field, no reuse | |
---|---|---|---|---|
ran test method | all fail | first fails | ok | ok |
ran test class | all fail | ok | ok | ok |
To Reproduce
@SpringBootIntegrationTest
class TestClass {
// LogCaptor logcaptor;
// static LogCaptor logcaptor;
// LogCaptor logcaptor = LogCaptor.forClass(Sut.class);
@Autowired
Sut sut;
@BeforeEach
void beforeEach() {
// logcaptor = LogCaptor.forClass(Sut.class);
// logcaptor.clearLogs();
}
private static Stream<List<String>> provideParameters() {
return Stream.of(
null,
List.of()
);
}
@ParameterizedTest
@MethodSource("provideParameters")
void testMethod(List<String> parameters) {
CompletableFuture<void> future = sut.doStuff(parameters);
assertThrows(Exception.class, () -> future.get(10, TimeUnit.SECONDS));
assertThat(logcaptor.getErrorLogs()).containsExactly("Expected error message");
}
}
In Sut:
@Async
public CompletableFuture<Void> doStuff(List<String> params) {
if (params == null || params.isEmpty()) {
var errorMessage = "Expected error message";
log.error(errorMessage);
return CompletableFuture.failedFuture(new Exception(errorMessage));
}
try {
// do stuff
} catch (Exception e) {
log.error(e.getMessage(), e);
return CompletableFuture.failedFuture(e);
}
return CompletableFuture.completedFuture(null);
}
Expected behavior I would have expected logCaptor.getErrorLogs to contain the expected error message no matter how the test method is run and no matter how the lagCaptor is defined and (re)used.
Environmental Data:
- Logcaptor 2.7.0
- junit 5.7.2
- jul-to-slf4j 1.7.32
- slf4j-api 1.7.32
- log4j-to-slf4j 2.14.1
- log4j-api 2.14.1
- Java 16.0.2
- Gradle 7.1.1
- Windows 10
Issue Analytics
- State:
- Created 2 years ago
- Comments:7 (4 by maintainers)
Sorry for the late answer, i was quite busy - but just wanted to return the thank you for the follow up 😃
Hi @hoangbnc
Thank you for the suggestion, however this won’t solve the root issue which is causing this different behavior. Adding a reset function and calling that in the before each is not different than just initializing it at that level. I think the ideal solution for this kind of use case or rather generic use case when using spring with junit 4 or 5 would be creating a junit-extension for logcaptor which would take away all of the verbosity for creating, resetting and disposal of logcaptor in the different test phases such as before all, before each, after each and after all.