Open source · alpha · 0.3.0-alpha2 on Maven Central · 0.3.0-alpha3-SNAPSHOT on main
Java cloud integration tests on local emulators.
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.
Run Java tests against a local emulator instead of a mock or a real cloud account. Tests use interfaces with no vendor SDK, so changing provider is meant to be a classpath change rather than a rewrite.
How it works
The module with the capability interfaces has no cloud SDK on its classpath. The AWS SDK is in a separate module that Java's ServiceLoader finds at runtime.
A test that calls storage.putObject(...) compiles against an interface, not the AWS client. A second provider would need only a different jar; none exists yet (see Where it stands).
<!-- the capabilities, the SPI and the JUnit 5 extension --><dependency><groupId>com.enrichmeai</groupId> <artifactId>test-core</artifactId><version>0.3.0-alpha2</version> <scope>test</scope></dependency><!-- the AWS adapter, found at runtime; your tests never import it --><dependency><groupId>com.enrichmeai</groupId> <artifactId>test-cloud-aws</artifactId><version>0.3.0-alpha2</version> <scope>test</scope></dependency>
@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
// abridged from examples/spring-boot in the repository@DynamicPropertySourcestatic void cloud(DynamicPropertyRegistry r) {Map<String, String> c = CloudAdapters.get(AWS, config).connectionProperties();r.add("app.storage.endpoint", () -> c.get(endpoint(STORAGE)));r.add("app.storage.access-key", () -> c.get(CREDENTIALS_KEY));}// Quarkus: return the same map from a QuarkusTestResourceLifecycleManager
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 capabilities. A method is included only if it can be implemented 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 verifyon JDK 17, which is also what CI builds on, is green with no skip flags and runs 85 tests: 49 unit, 29 integration against LocalStack, 7 Cucumber scenarios. Formatting, style, dependency convergence and coverage floors are all enforced in that one command. live - Coverage
- Measured: every line and every branch in the core module; line 0.93 and branch 0.75 in the AWS adapter. The target is 0.80 and 0.70, and both modules meet it. The floors the build enforces are those measured values, cut to two decimal places: 1.00 and 1.00 for the core, 0.92 and 0.74 for the adapter, so a drop below these values fails the build. What the adapter still misses is mostly provider-error paths the emulator does not produce on demand. target met
- Scope
- Cloud interactions and the application under test. A test drives a
BlobStoragedirectly to assert your code wrote what you meant, andconnectionProperties()hands your application the same emulator’s endpoints, region and credentials under provider-neutral keys, with no vendor SDK type, no Testcontainers type and nothing from an internal package. The repository has a worked example for each of Spring Boot 4 and Quarkus, built and tested in CI against every commit. Micronaut has no example. released - Known defects
- The multi-test-class case, which is the case it is aimed at, misbehaved: the emulator container is shared for the whole JVM, the JUnit extension never released topics or tables, and
ensureTabledid not check that an existing table's key schema matched the one asked for — so a second test class could silently inherit the first's table and fail later, somewhere else, with a message that named neither. Found by reading the code for this page's own backlog, listed here, and now fixed: every capability's resources are released after the class, a release that fails is reported with the resource name and cause instead of being swallowed, and a schema mismatch fails inensureTablenaming the table and both schemas. Two questions from that work are open and recorded: whether a failed teardown should fail the build, and whether a mismatch should recreate the table rather than refuse. fixed - 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
- Packages
com.enrichmeai.*, matching the Maven coordinates. They wereorg.deveasy.*until the release was prepared; the rename is free while nothing is published and permanent after the first publish, which is why it landed before the tag rather than after. done- Distribution
- On Maven Central. Merging a release pull request builds on JDK 17, runs the full verify, signs, uploads through the Sonatype Central Portal and tags the commit; a person presses Publish on the Portal as the last step, and did on 4 October 2026 for the first release and on 7 October for the second. published
- Artifacts
com.enrichmeai:test-core, the provider-neutral capabilities, the SPI and the JUnit 5 extension, 0.3.0-alpha2;com.enrichmeai:test-cloud-aws, the AWS adapter, 0.3.0-alpha2.test-featurehas no main sources and is not published.- 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. 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 →
What each release added
From the changelog, release by release. The full record, including every fix, is CHANGELOG.md →
0.3.0-alpha2 no API change
- Changed
- The coverage floors the build enforces rise to what the tests now measure: every line and branch in the core module, 0.92 line and 0.74 branch in the AWS adapter. Both modules meet the 0.80 / 0.70 target.
- Tests
- New tests for the paths a user meets when something is missing or fails: no
@WithCloud, an adapter without a capability, a table, key, bucket or queue that is not there, results across more than one page, and a race to start the emulator. One of them pinned a behaviour reported on its pull request: a DynamoDB number beyond thelongrange reads back as text.
0.3.0-alpha1 the first release, on Maven Central
- Added
- Your application under test can reach the emulator.
CloudAdapter.connectionProperties()returns the endpoints, region and credentials under provider-neutral keys, the constants inConnectionProperties, ready for a Spring Boot@DynamicPropertySourceor a QuarkusQuarkusTestResourceLifecycleManager. - Added
- Worked examples for Spring Boot 4 and Quarkus, standalone projects in
examples/that configure a real service only from those properties, built and tested in CI against every commit. - Changed
- Testcontainers 2, the line Spring Boot 4 and Quarkus 3.31 onwards manage. A project still on Testcontainers 1 gets 2 through this library.
- Breaking
- The Maven coordinates and the Java packages are
com.enrichmeai, no longerorg.deveasy; settled before the first publish because publishing makes them permanent.
Can I use this today?
Yes, as an alpha, against AWS in emulator mode: add test-core and test-cloud-aws from Maven Central with test scope (above), and have Docker running. What remains open is listed above rather than left for you to find.
Can I test my Spring Boot app with it?
Yes. connectionProperties() gives you the emulator’s endpoints, region and credentials; copy them into your application’s own keys from a @DynamicPropertySource, as the Spring Boot 4 example in the repository does. Quarkus works the same way through a test resource, and has its own example.
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?
The build fails if coverage drops below the floors shown above, which are the measured values.
Is it related to Cistern or Penstock?
No. It is a separate open-source testing tool. All products →