Test harness¶
mongoose-test-support wraps a booted MongooseServer as an AutoCloseable, with a builder for the common feed/sink/processor wiring and a small suite of await helpers. Replaces ~30 lines of boot/teardown ceremony per integration test with ~10.
<dependency>
<groupId>com.telamin</groupId>
<artifactId>mongoose-test-support</artifactId>
<version>1.0.35</version>
<scope>test</scope>
</dependency>
Quick start¶
import com.telamin.mongoose.plugin.testsupport.MongooseTestHarness;
import com.telamin.mongoose.connector.memory.InMemoryEventSource;
import com.telamin.mongoose.connector.memory.InMemoryMessageSink;
@Test
void echo() {
InMemoryEventSource<String> feed = new InMemoryEventSource<>();
InMemoryMessageSink captured = new InMemoryMessageSink();
try (MongooseTestHarness h = MongooseTestHarness.builder()
.feed("in", feed, "feed-agent")
.sink("out", captured)
.processor("processor-agent", "echo", new EchoHandler())
.start()) {
feed.offer("alpha");
feed.offer("beta");
h.awaitCondition(() -> captured.getMessages().size() >= 2);
assertEquals(List.of("echo:alpha", "echo:beta"), captured.getMessages());
}
}
What it replaces¶
A typical pre-harness integration test:
// build sources/sinks/processors
FileEventSource source = new FileEventSource();
source.setFilename(input.toString());
source.setReadStrategy(ReadStrategy.EARLIEST);
FileMessageSink sink = new FileMessageSink();
sink.setFilename(output.toString());
var feed = EventFeedConfig.builder()
.instance(source).name("file-feed").broadcast(true)
.agent("file-source-agent", new SleepingMillisIdleStrategy(1))
.build();
var sinkCfg = EventSinkConfig.builder()
.instance(sink).name("file-sink").build();
var processor = EventProcessorConfig.builder()
.customHandler(new ForwardingHandler())
.name("forwarder").build();
var app = MongooseServerConfig.builder()
.addProcessor("processor-agent", processor)
.addEventFeed(feed)
.addEventSink(sinkCfg)
.build();
MongooseServer server = MongooseServer.bootServer(app);
try {
long deadline = System.currentTimeMillis() + 3_000;
while (System.currentTimeMillis() < deadline) {
if (Files.exists(output)
&& Files.readAllLines(output).size() >= 3) break;
Thread.sleep(20);
}
assertEquals(expected, Files.readAllLines(output));
} finally {
server.stop();
}
The same test with the harness:
FileEventSource source = new FileEventSource();
source.setFilename(input.toString());
source.setReadStrategy(ReadStrategy.EARLIEST);
FileMessageSink sink = new FileMessageSink();
sink.setFilename(output.toString());
try (MongooseTestHarness h = MongooseTestHarness.builder()
.feed("file-feed", source, "file-source-agent")
.sink("file-sink", sink)
.processor("processor-agent", "forwarder", new ForwardingHandler())
.start()) {
h.awaitFileLines(output, 3);
assertEquals(expected, Files.readAllLines(output));
}
API surface¶
Builder¶
| Method | Purpose |
|---|---|
feed(name, source, agentName) |
Add an event feed |
feed(name, source, agentName, idleStrategy) |
Same with explicit idle strategy |
sink(name, sink) |
Add a message sink |
service(name, instance) |
Register service under the concrete class |
service(name, instance, serviceClass) |
Register under an interface — see serviceClass |
processor(agentName, handlerName, ObjectEventHandlerNode) |
Typed processor |
processor(agentName, handlerName, Consumer<Object>) |
Function-style processor |
defaultIdleStrategy(idleStrategy) |
Override the default (SleepingMillisIdleStrategy(1)) |
customise(fn) |
Escape hatch onto the raw MongooseServerConfig.Builder |
start() |
Build, boot, return harness |
Harness¶
| Method | Purpose |
|---|---|
awaitCondition(BooleanSupplier) |
Poll until true; default 3 s timeout |
awaitCondition(BooleanSupplier, Duration) |
Poll until true or AssertionError |
awaitFileLines(Path, int) |
Poll until file has N lines |
awaitFileLines(Path, int, Duration) |
Same with explicit timeout |
readLines(Path) |
Read file as UTF-8 lines (unchecked I/O) |
server() |
The underlying MongooseServer |
close() |
Stop server; idempotent |
MongooseTestHarness.wrap(server) |
Adopt an externally-booted server |
Conventions¶
- Always
try-with-resources—start()returnsAutoCloseable; don't manageserver.stop()manually. - Use
awaitCondition, notThread.sleep— sleeps are flaky on CI; condition polls succeed as soon as the predicate holds. - One harness per test — fresh server each time. Shared servers create ordering coupling.
@TempDirfor any file paths — auto-cleanup and isolation.
When to drop down¶
If you need something the builder doesn't expose:
try (MongooseTestHarness h = MongooseTestHarness.builder()
.feed("in", feed, "feed-agent")
.customise(b -> b
.addThread(new ThreadConfig("custom-thread", true))
.eventInvokeStrategy(CallBackType.ON_TRIGGER, MyStrategy::new))
.start()) {
// ...
}
customise(...) hands you the underlying MongooseServerConfig.Builder — every config method is available.