Integration Testing
Introduction
Integration tests ensure that Flamingock operates correctly in a real environment by executing changes against live systems — such as databases, cloud APIs, or internal services.
These tests involve spinning up the actual backend system and running Flamingock end-to-end:
- Change unit execution
- Audit log persistence
- Distributed lock acquisition
Integration tests should be used to validate that the full pipeline behaves as expected — from execution to rollback.
Example: Creating an S3 bucket
Suppose you have a change unit that creates an Amazon S3 bucket:
@Change(id = "create-bucket", author = "dev-team") // order extracted from filename
public class _0001__CreateS3BucketChange {
@Apply
public void apply(S3Client s3Client) {
s3Client.createBucket(CreateBucketRequest.builder()
.bucket("flamingock-test-bucket")
.build());
}
@Rollback
public void rollback(S3Client s3Client) {
s3Client.deleteBucket(DeleteBucketRequest.builder()
.bucket("flamingock-test-bucket")
.build());
}
}
Integration test with Testcontainers
To test this change end-to-end, we will:
- Spin up a MongoDB container to be used as Flamingock’s audit backend
- Inject a real S3 client (mocked, localstack, or real AWS)
- Configure Flamingock and execute it
class IntegrationTest {
static final MongoDBContainer mongoContainer = new MongoDBContainer("mongo:6.0");
@BeforeAll
static void initMongo() {
mongoContainer.start();
}
@AfterAll
static void tearDown() {
mongoContainer.stop();
}
@Test
void shouldExecuteChangeAgainstS3AndAuditToMongo() {
var s3Client = S3Client.builder()
.region(Region.EU_WEST_1)
.build();
var mongoClient = MongoClients.create(mongoContainer.getReplicaSetUrl());
Runner runner = Flamingock.builder()
.addDependency(s3Client)
.addDependency(mongoClient)
.setProperty("mongodb.databaseName", "test-db")
.build();
runner.execute();
// ✅ Verify the S3 bucket was created
ListBucketsResponse buckets = s3Client.listBuckets();
boolean bucketExists = buckets.buckets().stream()
.anyMatch(b -> b.name().equals("flamingock-test-bucket"));
assertTrue(bucketExists, "Expected S3 bucket was not found");
// ✅ Verify the change was audited in MongoDB
MongoDatabase db = mongoClient.getDatabase("test-db");
MongoCollection<Document> auditCollection = db.getCollection("flamingockAuditLog");
var document = new Document("changeId", "create-bucket")
.append("state","EXECUTED");
Document auditEntry = auditCollection.find(document).first();
assertNotNull(auditEntry, "Flamingock audit log entry was not found in MongoDB");
}
}
✅ Best practices
- Use Testcontainers to spin up a real audit backend (e.g., MongoDB) — this avoids the need for manual test setup
- Run Flamingock fully using
.build().execute()
— don’t call internal methods manually - Clean up the backend between tests or isolate data with unique test identifiers
- Validate changes by checking the actual target system or using custom assertions
- Use integration tests sparingly — unit tests are faster and should cover most logic