Runtime integration testing for Java microservices.
Situs lets you define integration tests alongside your application and execute them inside a running microservice environment.
Instead of only testing integrations during CI, Situs lets you verify that your deployed service can actually communicate with the systems around it:
- other microservices
- REST APIs
- databases
- Kafka and message brokers
- authentication services
- external APIs
- infrastructure dependencies
Think of it as:
JUnit-style tests for your running microservice environment.
Unit tests tell you that your code works.
Build-time integration tests tell you that your application works in a controlled test environment.
But many microservice failures only appear after deployment.
For example:
Wrong service URL
↓
Broken DNS / service discovery
↓
Missing secret
↓
Invalid certificate
↓
Kafka ACL problem
↓
Network policy blocking traffic
↓
Different API version deployed
↓
Environment-specific configuration error
These problems are difficult to catch with tests that only run during the build.
Situs runs tests inside the deployed application, using the same configuration and dependencies as the service itself.
Kubernetes / Docker / VM
┌─────────────────────────────────────────────────────┐
│ │
│ ┌───────────────────────────────┐ │
│ │ Order Service │ │
│ │ │ │
│ │ Application code │ │
│ │ │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ Situs tests │ │ │
│ │ └────────────┬────────────┘ │ │
│ └───────────────┼───────────────┘ │
│ │ │
│ ┌────────┼────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ Payment Kafka Database │
│ Service │
│ │ │
│ ▼ │
│ External API │
│ │
└─────────────────────────────────────────────────────┘
You're testing the system where it actually runs.
Deploy a microservice and immediately verify that its critical integrations work.
Build
↓
Unit tests
↓
Deploy
↓
Run Situs
↓
PASS ───────→ Continue rollout
│
FAIL
↓
Stop / rollback
Verify that one service can actually communicate with another.
For example:
Order Service
│
├──► Payment Service
│
├──► Inventory Service
│
├──► Kafka
│
└──► PostgreSQL
A Situs test can verify all of these from the same runtime environment as your application.
Use Situs to verify environments such as:
development
test
QA
staging
preview environments
ephemeral environments
production-like environments
The same test suite can be executed after every deployment.
Expose lightweight tests that verify the most important functionality before traffic reaches a deployment.
For example:
✓ database reachable
✓ Kafka reachable
✓ authentication service reachable
✓ payment service reachable
✓ external provider credentials valid
Situs can be triggered over HTTP, making it easy to integrate with deployment pipelines.
CI
│
▼
Build
│
▼
Deploy
│
▼
POST /api/test-framework/suites/DeploymentVerification/run
│
▼
Poll test status
│
├── PASS → continue
│
└── FAIL → stop deployment
Gradle Kotlin DSL:
dependencies {
implementation("no.kompilator:situs:2.0.0")
}Optional reporting support:
dependencies {
implementation("no.kompilator:plugins:2.0.0")
}The plugins module includes support for:
- JUnit XML
- Open Test Reporting XML
- JSON
A Situs suite is a normal Java class.
@TestSuite(
name = "OrderServiceIntegration",
description = "Integration checks for the Order Service"
)
public class OrderServiceIntegration {
@Test(name = "application-is-running")
public void applicationIsRunning() {
assertThat(true).isTrue();
}
}Situs integrates directly with Spring.
That means your test suites can use the same Spring beans, clients and configuration as your application.
@Component
@TestSuite(name = "PaymentServiceIntegration")
public class PaymentServiceIntegration {
private final PaymentClient paymentClient;
public PaymentServiceIntegration(
PaymentClient paymentClient
) {
this.paymentClient = paymentClient;
}
@Test(
name = "payment-service-is-reachable",
timeout = "PT5S"
)
public void paymentServiceIsReachable() {
var health = paymentClient.health();
assertThat(health.isHealthy()).isTrue();
}
}This allows your tests to use the application's real:
HTTP clients
authentication
configuration
service URLs
TLS configuration
serializers
connection pools
database configuration
message broker configuration
instead of recreating them in a separate test harness.
Configure which packages Situs should scan:
testframework.scan-packages=com.example.testsSpring Boot auto-configuration is enabled automatically when Situs is on the classpath.
For non-Boot Spring applications you can explicitly enable runtime tests using:
@EnableRuntimeTestsUseful configuration:
testframework.scan-packages=com.example.tests
testframework.full-classpath-scan=false
testframework.max-stored-runs=200
# Require "Authorization: Bearer <token>" on the HTTP API (except /status)
testframework.api-token=${SITUS_API_TOKEN}
# Keep finished run results on disk so they survive restarts
testframework.result-directory=/var/lib/situs/runs
testframework.reporting.enabled=true
testframework.reporting.output-dir=build/test-reports
testframework.reporting.formats=JUNIT_XML,OPEN_TEST_REPORTING_XML,JSONSitus exposes an HTTP API through Spring Boot.
curl \
http://localhost:8080/api/test-framework/suitescurl -X POST \
http://localhost:8080/api/test-framework/suites/PaymentServiceIntegration/runResponse:
{
"runId": "abc-123"
}curl \
http://localhost:8080/api/test-framework/runs/abc-123/statuscurl -X POST \
http://localhost:8080/api/test-framework/runs/abc-123/cancelPOST /suites/{suiteName}/gate runs a suite, waits for it to finish, and reports the outcome in the HTTP status. It is the simplest option for a pipeline step, because no polling is needed:
curl -f -X POST \
"http://localhost:8080/api/test-framework/suites/DeploymentVerification/gate?timeout=PT2M"| Status | Meaning |
|---|---|
200 |
Suite completed and every test passed |
422 |
Suite completed with one or more failing tests |
500 |
The run itself failed |
504 |
The suite did not finish within timeout (default PT5M); the run is cancelled |
The body is always the run status, so pipelines can log the failing tests. The request blocks until the run ends, so give the HTTP client a timeout longer than timeout.
Set testframework.api-token (or the environment variable TESTFRAMEWORK_API_TOKEN) and send the token with every request except /status:
curl -H "Authorization: Bearer $SITUS_API_TOKEN" \
http://localhost:8080/api/test-framework/suitesRequests without the correct token get 401 Unauthorized. If no token is configured, the API is open and a warning is logged at startup.
Consider an Order Service which depends on:
Order Service
│
├── Payment Service
│
├── Inventory Service
│
├── PostgreSQL
│
└── Kafka
You can create a deployment verification suite:
@Component
@TestSuite(
name = "DeploymentVerification",
description = "Verify runtime dependencies"
)
public class DeploymentVerification {
private final PaymentClient payment;
private final InventoryClient inventory;
private final JdbcTemplate database;
public DeploymentVerification(
PaymentClient payment,
InventoryClient inventory,
JdbcTemplate database
) {
this.payment = payment;
this.inventory = inventory;
this.database = database;
}
@Test(
name = "payment-service",
timeout = "PT5S"
)
public void paymentService() {
assertThat(
payment.health().isHealthy()
).isTrue();
}
@Test(
name = "inventory-service",
timeout = "PT5S"
)
public void inventoryService() {
assertThat(
inventory.health().isHealthy()
).isTrue();
}
@Test(
name = "database",
timeout = "PT5S"
)
public void database() {
Integer result =
database.queryForObject(
"SELECT 1",
Integer.class
);
assertThat(result).isEqualTo(1);
}
}After deployment:
curl -X POST \
http://order-service:8080/api/test-framework/suites/DeploymentVerification/runNow the service verifies its dependencies from inside the deployed environment.
Situs supports parameterized tests.
@ParameterizedTest(
name = "service[{index}] {0}"
)
@ValueSource(strings = {
"payment",
"inventory",
"shipping"
})
public void serviceIsReachable(
String service
) {
assertThat(
serviceRegistry.isHealthy(service)
).isTrue();
}Supported sources:
@ValueSource
@CsvSource
@CsvFileSource
@MethodSource
@EnumSource
@NullSource
@EmptySource
@NullAndEmptySource
Each generated invocation is treated as a separate test case for:
- execution
- reporting
- discovery
- HTTP responses
@ParameterizedTest(
name = "addition[{index}] {0}+{1}={2}"
)
@CsvSource({
"1,2,3",
"2,3,5",
"40,2,42"
})
public void addition(
int left,
int right,
int expected
) {
assertThat(
left + right
).isEqualTo(expected);
}@ParameterizedTest(
name = "multiply[{index}] {0}*{1}={2}"
)
@MethodSource("cases")
public void multiply(
int left,
int right,
int expected
) {
assertThat(
left * right
).isEqualTo(expected);
}
static Stream<Arguments> cases() {
return Stream.of(
Arguments.of(2, 3, 6),
Arguments.of(7, 6, 42)
);
}Situs supports familiar lifecycle annotations.
@BeforeAll
public void beforeSuite() {
}
@BeforeEach
public void beforeTest() {
}
@Test
public void test() {
}
@AfterEach
public void afterTest() {
}
@AfterAll
public void afterSuite() {
}Suites can execute tests in parallel.
@TestSuite(
name = "DependencyChecks",
parallel = true
)
public class DependencyChecks {
@Test
public void payment() {
}
@Test
public void inventory() {
}
@Test
public void shipping() {
}
}This is useful when validating multiple independent microservice dependencies.
Tests can use millisecond or ISO-8601 duration timeouts.
@Test(
name = "fast-check",
timeoutMs = 500
)
public void fastCheck() {
}Or:
@Test(
name = "external-api",
timeout = "PT30S"
)
public void externalApi() {
}Examples:
PT0.5S = 500 ms
PT30S = 30 seconds
PT5M = 5 minutes
PT1H = 1 hour
Transient failures can automatically be retried.
@Test(
name = "external-service",
retries = 2
)
public void externalService() {
assertThat(
client.health()
).isTrue();
}Retries are useful for dependencies which may take a short time to become ready during deployment.
Tests can wait before execution.
@Test(
name = "service-ready",
delayMs = 500
)
public void serviceReady() {
}Tests can explicitly define execution order.
@Test(
name = "create-order",
order = 1
)
public void createOrder() {
}
@Test(
name = "verify-order",
order = 2
)
public void verifyOrder() {
}
@Test(
name = "delete-order",
order = 3
)
public void deleteOrder() {
}Tests with lower order values run first.
Ties are resolved using the method name.
Add the reporting plugin:
dependencies {
implementation("no.kompilator:plugins:2.0.0")
}Configure output:
testframework.reporting.enabled=true
testframework.reporting.output-dir=build/test-reports
testframework.reporting.formats=
JUNIT_XML,
OPEN_TEST_REPORTING_XML,
JSONSupported formats:
JUnit XML
Open Test Reporting XML
JSON
Reports are automatically generated after suite execution.
Reporting plugins can also be created manually.
ReportingPlugin reporter =
ReportingPlugin.builder()
.outputDir(
Path.of("build/test-reports")
)
.format(
ReportFormat.JUNIT_XML
)
.format(
ReportFormat.JSON
)
.build();
testFrameworkService.addListener(
reporter
);Plugins can observe test progress using:
SuiteRunListener#onTestCompleted(...)Asynchronous execution exposes runtime progress information.
For example:
completedCount
totalCount
runStartedAtEpochMs
lastUpdatedAtEpochMs
startedAtEpochMs
completedAtEpochMs
Runs can finish with statuses such as:
COMPLETED
CANCELLED
Situs works with Kotlin.
@Component
@TestSuite(
name = "PaymentServiceIntegration"
)
class PaymentServiceIntegration(
private val paymentClient: PaymentClient
) {
@Test(
name = "payment-service-is-reachable"
)
fun paymentServiceIsReachable() {
assertThat(
paymentClient.health().isHealthy
).isTrue()
}
}When using Spring, use:
kotlin("plugin.spring")so Spring-managed classes can be proxied correctly.
Situs is not intended to replace normal unit or integration tests.
It fills a different part of the testing lifecycle.
| Capability | Traditional CI integration tests | Situs |
|---|---|---|
| Unit-level testing | ✓ | — |
| Runs during build | ✓ | Optional |
| Runs after deployment | Usually not | ✓ |
| Uses deployed service configuration | Usually not | ✓ |
| Uses real service discovery | Limited | ✓ |
| Tests runtime networking | Limited | ✓ |
| Tests environment secrets/configuration | Limited | ✓ |
| Tests real microservice connectivity | Limited | ✓ |
| Trigger tests over HTTP | Usually not | ✓ |
| Spring dependency injection | ✓ | ✓ |
| Deployment verification | Indirect | ✓ |
| Machine-readable reports | ✓ | ✓ |
A typical setup might therefore look like:
TESTING PIPELINE
Source code
│
▼
Unit tests
│
▼
Integration tests
│
▼
Build
│
▼
Deploy
│
▼
Situs tests
│
▼
Runtime verified
Testcontainers is excellent for creating temporary infrastructure during automated tests.
For example:
JUnit
│
├── temporary PostgreSQL
├── temporary Kafka
└── temporary Redis
Situs solves a different problem.
Deployed application
│
├── real PostgreSQL
├── real Kafka
├── real service discovery
├── real authentication
└── real microservices
Testcontainers answers:
Does my application work with these dependencies in a controlled test environment?
Situs answers:
Does my deployed application actually work with the dependencies configured in this environment?
The two approaches can complement each other.
A typical Kubernetes deployment might look like:
┌──────────────── Kubernetes ─────────────────┐
│ │
│ ┌──────────────┐ │
│ │ Order Service│ │
│ │ │ │
│ │ Situs │ │
│ └──────┬───────┘ │
│ │ │
│ ┌─────┼──────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ Payment Inventory Kafka │
│ Service Service │
│ │
└─────────────────────────────────────────────┘
After the deployment becomes ready:
curl -X POST \
http://order-service/api/test-framework/suites/DeploymentVerification/runThe result can then be used by your deployment pipeline to determine whether rollout should continue.
| Feature | Situs |
|---|---|
| Test suites | @TestSuite |
| Tests | @Test |
| Parameterized tests | @ParameterizedTest |
| Setup / teardown | @BeforeAll, @BeforeEach, @AfterEach, @AfterAll |
| Parallel execution | ✓ |
| Deterministic ordering | ✓ |
| Timeouts | ✓ |
| Delays | ✓ |
| Retries | ✓ |
| Spring dependency injection | ✓ |
| Package-based discovery | ✓ |
| Runtime HTTP API | ✓ |
| Run cancellation | ✓ |
| JUnit XML reports | ✓ |
| Open Test Reporting XML | ✓ |
| JSON reports | ✓ |
| Java | ✓ |
| Kotlin | ✓ |
.
├── situs/
│ Core runtime testing library
│
├── plugins/
│ Reporting plugins
│
├── java-spring-boot-sample-app/
│ Java Spring Boot example
│
└── kotlin-spring-boot-sample-app/
Kotlin Spring Boot example
| Module | Artifact | Description |
|---|---|---|
situs |
no.kompilator:situs |
Runtime test engine, annotations, Spring integration and HTTP API |
plugins |
no.kompilator:plugins |
JUnit XML, OTR XML and JSON reporting |
java-spring-boot-sample-app |
— | Java example |
kotlin-spring-boot-sample-app |
— | Kotlin example |
Published artifacts:
Supported packages:
no.kompilator.situs.annotations
no.kompilator.situs.model
no.kompilator.situs.params
no.kompilator.situs.plugin
no.kompilator.situs.service
no.kompilator.situs.spring
no.kompilator.situs.spring.model
Internal packages may change without notice:
no.kompilator.situs.domain
no.kompilator.situs.runtime
Applications should build against supported packages only.
Situs fails fast during suite registration when it detects invalid configuration.
Examples include:
- duplicate suite names
- duplicate test names
- invalid timeouts
- negative delays
- negative retry counts
- invalid lifecycle methods
- static test methods
- parameterized tests without argument sources
- invalid parameter sources
This helps configuration errors appear at application startup instead of during test execution.
Build everything:
./situs/gradlew --project-dir . buildBuild the core library:
./situs/gradlew :situs:buildBuild plugins:
./situs/gradlew :plugins:build./situs/gradlew :situs:test./situs/gradlew :plugins:testOr run verification across modules:
./situs/gradlew testAll./situs/gradlew buildAll./situs/gradlew publishAllToMavenLocalPublishing to Maven Central requires credentials and signing configuration.
Configure:
centralUsername=...
centralPassword=...
signingKey=...
signingPassword=...Or environment variables:
export CENTRAL_USERNAME=...
export CENTRAL_PASSWORD=...
export SIGNING_KEY=...
export SIGNING_PASSWORD=...Publish:
./situs/gradlew publishReleaseAvailable release tasks:
releaseCheck
publishAllToMavenLocal
publishRelease
GitHub Actions releases can be triggered by pushing a version tag:
git tag v2.0.0
git push origin v2.0.0| Requirement | Version |
|---|---|
| Java | 21 |
| Gradle | Wrapper included |
| Spring Boot | 4.0.x optional |
The core Situs execution engine does not require Spring.
situs/README.md— annotations, runtime engine and API referenceplugins/README.md— reportingjava-spring-boot-sample-app— Java examplekotlin-spring-boot-sample-app— Kotlin example
Modern microservices are tested extensively before deployment.
But deployment itself introduces another layer of failure:
Code
+
Configuration
+
Infrastructure
+
Networking
+
Authentication
+
Other services
=
The actual running system
Situs gives the running application a way to verify those assumptions.
Test your microservices where they actually run.