Testing Resources
Register a stub with the same resource() call used in production, paired with InMemoryFlexiQ.
Register a stub with the same resource() call used in production, paired with InMemoryFlexiQ.
Resources are tested the same way they're built: register a stub factory
under the resource's name with flexiq.resource(name, factory). Handlers
resolve by name — Resources.use("db") or a @Resource("db") parameter — so
they can't tell a fake from the real thing.
There's no separate mock-resource type and no test-only override API.
Anything a factory can return works as a fake — an AtomicInteger for call
counts, a hand-written stub, or a mock from a library such as Mockito.
InMemoryFlexiQorg.byteveda:flexiq-test opens a FlexiQ over a pure-JVM in-memory
backend — no JNI, no disk. The resource runtime is backend-independent, so
resources build, scope, and tear down exactly the same way over it:
testImplementation("org.byteveda:flexiq-test:1.0.0")import org.byteveda.flexiq.test.InMemoryFlexiQ;
@Test
void injectsAFakeDatabase() throws Exception {
AtomicInteger builds = new AtomicInteger();
try (FlexiQ flexiq = InMemoryFlexiQ.open()) {
flexiq.resource("db", ctx -> {
builds.incrementAndGet();
return new FakeDatabase();
});
try (Worker worker = flexiq.worker()
.handle("sync_user", String.class, userId -> {
FakeDatabase db = Resources.use("db");
return db.markSynced(userId);
})
.start()) {
String id = flexiq.enqueue("sync_user", "u_123");
Job job = flexiq.awaitJob(id, Duration.ofSeconds(5)).orElseThrow();
assertEquals(JobStatus.COMPLETE, job.status);
}
assertEquals(1, builds.get());
assertEquals(1, flexiq.resourceMetrics().get("db").created());
}
}flexiq.resource(name, ...) throws ResourceException if name is
already registered — registration is a one-time call per FlexiQ client,
not something a later call can override:
flexiq.resource("db", ctx -> realPool());
flexiq.resource("db", ctx -> new FakeDatabase()); // throws ResourceExceptionThat means you can't layer a fake over a resource your production wiring
already registered on the same client — a shared configureQueue() helper,
say, that also registers "db". A test needs its own FlexiQ (and
therefore its own Worker) with the stub registered from scratch:
// Don't — configureProduction() already registered "db"; this throws.
FlexiQ flexiq = configureProduction(InMemoryFlexiQ.open());
flexiq.resource("db", ctx -> new FakeDatabase());
// Do — build the test's own client and register the stub first.
try (FlexiQ flexiq = InMemoryFlexiQ.open()) {
flexiq.resource("db", ctx -> new FakeDatabase());
// ... register handlers, run the worker ...
}If a production wiring method registers resources, take the factories (or the resource names it needs) as parameters instead of hard-coding them, so a test can supply stubs before calling it rather than fighting the same name twice.
See dependency injection for
how resource() and scopes work, and
testing for the full InMemoryFlexiQ
worker setup — awaitJob, event listeners, and when to fall back to a real
SQLite-backed integration test.