Unit Testing
Introduction
Unit tests focus on verifying the internal logic of a single change unit, without relying on any external system.
They are fast, isolated, and ideal for validating:
- That the
@Apply
method performs the correct logic - That the
@Rollback
method compensates properly on failure - That injected dependencies are used as expected (using mocks or fakes)
Unit tests are most useful when your change unit contains business logic, computation, validation, or decisions.
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());
}
}
Writing a unit test
To unit test this class, we use JUnit and a mocking library (e.g., Mockito).
We'll mock the S3Client
and verify the correct calls were made.
class _0001__CreateS3BucketChangeTest {
private final S3Client s3Client = mock(S3Client.class);
private final CreateS3BucketChange change = new CreateS3BucketChange();
@Test
void shouldCallCreateBucketOnExecution() {
var s3Client = mock(S3Client.class);
new _0001__CreateS3BucketChange().apply(s3Client);
verify(s3Client).createBucket(argThat(req ->
req.bucket().equals("flamingock-test-bucket")));
}
@Test
void shouldCallDeleteBucketOnRollback() {
var s3Client = mock(S3Client.class);
new _0001__CreateS3BucketChange().rollback(s3Client);
verify(s3Client).deleteBucket(argThat(req ->
req.bucket().equals("flamingock-test-bucket")));
}
}
✅ Best practices
- Use mocks or fakes to isolate the dependencies used in your change unit
- Focus only on the logic inside the
@Apply
and@Rollback
methods - Keep assertions specific and minimal — check that the right dependencies are called
- Avoid testing Flamingock itself (e.g., locking or audit behavior — that’s handled in integration tests)
- Use descriptive test names like
shouldCallCreateBucketOnExecution()
for readability