Open source · private alpha · 0.3.0-alpha1-private.1
Cloud tests that never name a cloud.
A Java test asks for a bucket or a queue and gets one, backed by a container the library started. Nothing on its compile classpath knows which cloud that was.
Testing cloud-backed Java usually means choosing between a mock that tests itself and a real account that is slow, costly and unavailable offline. This takes the third path: real service semantics against a local emulator, behind interfaces that carry no vendor SDK. A test compiled against those interfaces has no compile-time knowledge of a provider, so changing provider is a classpath change rather than a rewrite.
The seam is the product
Testcontainers already runs LocalStack. That part is not the contribution. What this adds is a dependency direction it enforces: the module holding the capability interfaces has no cloud SDK on its classpath at all, and the AWS SDK lives in a separate module found at runtime through Java's own ServiceLoader.
The consequence is small to describe and hard to retrofit. A test that says storage.putObject(...) compiles against an interface, not against Amazon's client. The same test would run on a second provider by putting a different jar on the classpath. Whether that claim survives contact with a second implementation is the open question below, and the repository says so rather than assuming it.
@WithCloud(provider = AWS, mode = EMULATOR)class OrderServiceIT {@Testvoid storesTheReceipt(BlobStorage storage) {storage.ensureBucket("receipts");storage.putObject("receipts", "o-1.json", body, "application/json");assertTrue(storage.exists("receipts", "o-1.json"));}}
Given cloud provider is "aws"And cloud mode is "emulator"And cloud region is "eu-west-1"# the steps below mention no AWS service by name
The extension resolves the parameter, ServiceLoader finds the AWS adapter on the classpath, and the adapter starts one LocalStack container per JVM and shares it. Docker has to be running. There is no credential to configure, because there is no account.
Four, deliberately small. A method earns its place only if it is implementable on more than one provider's emulator.
- BlobStorage
- Ensure and delete a bucket, put an object from bytes or a stream, get, delete, list by prefix, test existence. On AWS this is S3. works
- Queue
- Ensure and delete a queue, send, receive, receive with a timeout. On AWS this is SQS. works
- PubSub
- Ensure and delete a topic, ensure a subscription, publish, receive with or without a timeout. On AWS this is SNS delivering to SQS. works
- NoSqlTable
- Ensure a table with a partition key and optional sort key, put, get, delete, scan, query. On AWS this is DynamoDB. works
- Secrets, key management
- Named in the service-type enum with no interface behind them. Placeholders, not features. not built
- Build
- A plain
mvn -B verifyis green with no skip flags and runs 21 tests: 8 unit, 6 integration against LocalStack, 7 Cucumber scenarios. Formatting, style, dependency convergence and coverage floors are all enforced in that one command. live - Coverage
- Line 0.58 and branch 0.30 in the core module; line 0.66 and branch 0.35 in the AWS adapter. The target is 0.80 and 0.70 and neither module meets it. The build enforces the measured numbers as a floor that can only move up, so the gate is honest rather than aspirational. below target
- Scope
- It tests cloud interactions, not applications. A test asks for a
BlobStorageand drives it directly, so you can assert your code writes what you meant. It cannot point a Spring Boot, Quarkus or Micronaut context at the emulator: no endpoint and no credential is reachable from the provider-neutral API, and the only route to one crosses into an internal package and undoes the neutrality the library exists for. Closing that is specified and undecided. not yet - Known defects
- The multi-test-class case, which is the case it is aimed at. The emulator container is shared for the whole JVM, the JUnit extension never releases topics or tables, and
ensureTabledoes not check that an existing table's key schema matches the one asked for — so a second test class can silently inherit the first's table and fail later, somewhere else, with a message that names neither. Found by reading the code for this page's own backlog, and recorded rather than left for a user to hit. open - Providers
- One. The enum names Azure and GCP; no adapter implements either. Until a second exists, the claim that a test is portable across providers is a design intention, not a demonstrated property. That is the largest open risk in the architecture and it is recorded as such. one only
- Modes
- Emulator only. A live mode is declared in the enum with no tested path behind it, and it needs a credential story and a guardrail against destructive operations before it means anything. emulator only
- Distribution
- Not published. No Maven Central release, no private registry. Build it from source. unpublished
- Supply chain
- An OWASP Dependency-Check audit is wired into CI behind a profile, and is skipped until an NVD API key is configured. It has not yet audited anything, and the page says so rather than implying a clean report. not yet run
The planning documents are in the repository rather than summarised here: a product brief, a PRD with numbered requirements, an architecture spine with its decisions, and a backlog of eight epics covering the gaps above. Read the specs →
Can I use this today?
Only by building it from source, and only against AWS in emulator mode. It is a private alpha and nothing is published. It also has open defects in the several-test-classes-in-one-run case, which is listed above rather than left for you to find.
Can I test my Spring Boot app with it?
Not yet. It drives cloud capabilities directly; it does not configure an application under test, because nothing in the provider-neutral API hands you an endpoint or a credential. For that today, use Testcontainers' LocalStack module. Adding a provider-neutral connection accessor is specified and open.
Why not just mock the SDK?
Because a mock passes whether or not the key encoding, the pagination or the visibility timeout is right. The point of running against an emulator is that the test can fail for a real reason.
Do I need a cloud account?
No, and there is nowhere to put credentials. Docker has to be running; the first run pulls a LocalStack image of roughly 1.3 GB.
Is it really provider-neutral?
By construction, yes: the core module has no vendor SDK and adapters are found at runtime. By demonstration, not yet. One adapter cannot prove portability, and the repository treats that as its main open risk rather than a settled claim.
Why is the coverage number on this page?
Because the alternative was a badge that says nothing. The floors in the build are the measured values, so they cannot quietly slip, and the distance to the target is visible instead of hidden.
How does it relate to the rest of the register?
It does not. It is a testing tool, not part of the data-access line. It sits here because it is ours and it is open. The register →