Skip to content

Commit 0164b3a

Browse files
committed
feat: add the LogTestProcessesOutputExtension for convenience
1 parent 27a568b commit 0164b3a

File tree

3 files changed

+67
-1
lines changed

3 files changed

+67
-1
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,23 @@ public class MyTest {
249249
For more examples, see the [functional tests](https://github.com/netmikey/testprocesses/tree/main/testprocesses-core/src/test/java/io/github/netmikey/testprocesses/functional) in the `testprocesses-core` module.
250250

251251

252+
### Automatically logging a process' stdOut/stdErr streams
253+
254+
Testprocesses can automatically log the content of a test process' stdOut/stdErr streams. This can be done in 2 ways.
255+
256+
To log the full stdOut/stdErr streams of each process when it terminates, you can set the logger `io.github.netmikey.testprocesses.TestProcessesRegistry` to the `TRACE` level. This is useful to obtain diagnostic information if a process crashes on startup.
257+
258+
Alternatively, you can extend your test(s) with the `io.github.netmikey.testprocesses.extensions.LogTestProcessesOutputExtension` JUnit Jupiter extension. This logs only the part of each process' stdOut/stdErr streams after each test that was emitted during that test. This is useful to associate the part of a testprocess' output with the exact test that caused it.
259+
260+
```java
261+
@SpringBootTest
262+
@ExtendWith(LogTestProcessesOutputExtension.class)
263+
@TestProcess(SomeTestProcessDefinition.class)
264+
public class MyTest {
265+
// ...
266+
}
267+
```
268+
252269
## Limitations
253270

254271
Because the framework tries to reuse running test processes between tests (if not told otherwise), a test process becomes a shared resource and is, by nature, quite stateful. Because of this, running tests in parallel will most probably not behave as expected.

testprocesses-core/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ dependencyManagement {
1414
dependencies {
1515
implementation("jakarta.annotation:jakarta.annotation-api")
1616
implementation("org.slf4j:slf4j-api")
17+
implementation("org.junit.jupiter:junit-jupiter")
1718
implementation("org.springframework:spring-beans")
1819
implementation("org.springframework:spring-context")
1920
implementation("org.springframework:spring-core")
2021
implementation("org.springframework:spring-test")
2122
implementation("org.springframework.boot:spring-boot-autoconfigure")
2223

2324
testImplementation("org.springframework.boot:spring-boot-starter-test")
24-
testImplementation("org.junit.jupiter:junit-jupiter")
2525

2626
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
2727
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package io.github.netmikey.testprocesses.extensions;
2+
3+
import org.junit.jupiter.api.extension.AfterEachCallback;
4+
import org.junit.jupiter.api.extension.Extension;
5+
import org.junit.jupiter.api.extension.ExtensionContext;
6+
import org.springframework.test.context.junit.jupiter.SpringExtension;
7+
import org.springframework.util.StringUtils;
8+
9+
import io.github.netmikey.testprocesses.TestProcessDefinition;
10+
import io.github.netmikey.testprocesses.TestProcessDefinitionBy;
11+
import io.github.netmikey.testprocesses.TestProcessesRegistry;
12+
import io.github.netmikey.testprocesses.utils.StreamStart;
13+
14+
/**
15+
* A JUnit {@link Extension} that logs <code>stdOut</code> and
16+
* <code>stdErr</code> streams of each currently running test process after each
17+
* test. Only the output that has been produced on the stdOut/stdErr streams
18+
* during the current test is logged. If no output has been produced on a
19+
* stream, it is not logged.
20+
*/
21+
public class LogTestProcessesOutputExtension implements AfterEachCallback {
22+
23+
private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory
24+
.getLogger(LogTestProcessesOutputExtension.class);
25+
26+
@Override
27+
public void afterEach(ExtensionContext context) throws Exception {
28+
TestProcessesRegistry registry = SpringExtension.getApplicationContext(context)
29+
.getBean(TestProcessesRegistry.class);
30+
registry.runningProcessIdentifiers().forEach(processIdentifier -> {
31+
TestProcessDefinitionBy<? extends TestProcessDefinition> selector = TestProcessDefinitionBy
32+
.processIdentifier(processIdentifier);
33+
logStream(processIdentifier, "stdOut", registry.stdOutAsStringOf(selector, StreamStart.CURRENT_TEST));
34+
logStream(processIdentifier, "stdErr", registry.stdErrAsStringOf(selector, StreamStart.CURRENT_TEST));
35+
});
36+
}
37+
38+
private void logStream(String processIdentifier, String streamName, String content) {
39+
if (StringUtils.hasText(content)) {
40+
String title = processIdentifier;
41+
String delimiter = "-------------------- %s %s --------------------"
42+
.formatted(title, streamName);
43+
LOG.info("\n{}\n{}\n{}",
44+
delimiter,
45+
content.replaceAll("\r?\n$", "").replaceAll("(^|\r?\n)", "$1 "),
46+
"-".repeat(delimiter.length()));
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)