Open source · alpha · 0.3.0-alpha2 on Maven Central · 0.3.0-alpha3-SNAPSHOT on main

An alpha, on Maven Central. One provider, AWS. Emulator only. It tests your cloud calls, and it can hand your application under test the emulator’s endpoints and credentials. One adapter cannot yet prove the seam holds for a second cloud.

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.

What the alpha does
Four capabilities against AWS, run on LocalStack via Testcontainers, from JUnit 5 or Cucumber.
The idea01

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).

A test02
mavenJava 17 · Docker running
<!-- 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>
javaJUnit 5 · no provider named
@WithCloud(provider = AWS, mode = EMULATOR)
class OrderServiceIT {
@Test
void storesTheReceipt(BlobStorage storage) {
storage.ensureBucket("receipts");
storage.putObject("receipts", "o-1.json", body, "application/json");
assertTrue(storage.exists("receipts", "o-1.json"));
}
}
gherkinthe same infrastructure, from a feature file
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
javayour Spring Boot app, pointed at the same emulator
// abridged from examples/spring-boot in the repository
@DynamicPropertySource
static 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.

Capabilities03

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
Where it stands04
Build
A plain mvn -B verify on 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 BlobStorage directly to assert your code wrote what you meant, and connectionProperties() 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 ensureTable did 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 in ensureTable naming 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 were org.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-feature has 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's new05

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 the long range 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 in ConnectionProperties, ready for a Spring Boot @DynamicPropertySource or a Quarkus QuarkusTestResourceLifecycleManager.
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 longer org.deveasy; settled before the first publish because publishing makes them permanent.
FAQ06

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 →