Interview question
Verify PostgreSQL behavior with a Spring Boot Testcontainers test
Runs a Spring Boot integration test against PostgreSQL and proves a migration-backed database constraint at flush time.
TL;DR
Runs a Spring Boot integration test against PostgreSQL and proves a migration-backed database constraint at flush time.
Testcontainers, @ServiceConnection, real database semantics, migrations, flush behavior, constraints, and integration-test boundaries.
Practice the problem like a real interview: restate, reason, implement, and test.
Write an integration test proving the production migration enforces case-insensitive email uniqueness. Run the application against PostgreSQL through Testcontainers, let the normal migration tool create the schema, insert User@Example.com, then prove user@example.com violates the constraint when flushed.
Move to the linked follow-up, next path step, prerequisite, or deeper variant.
Practice the next layer of the same subject.
Answer a similar question in a different context.
save User@Example.com -> succeeds
save user@example.com -> DataIntegrityViolationException on flush
database engine -> PostgreSQL, not H2
@ServiceConnection so Spring Boot wires the datasource.saveAndFlush.@org.testcontainers.junit.jupiter.Testcontainers
@org.springframework.boot.test.context.SpringBootTest
class UserEmailConstraintTest {
@org.testcontainers.junit.jupiter.Container
@org.springframework.boot.testcontainers.service.connection.ServiceConnection
static final org.testcontainers.containers.PostgreSQLContainer<?> postgres =
new org.testcontainers.containers.PostgreSQLContainer<>("postgres:16-alpine");
@org.springframework.beans.factory.annotation.Autowired UserRepository users;
@org.junit.jupiter.api.Test
void email_uniqueness_is_case_insensitive() {
users.saveAndFlush(new User("User@Example.com"));
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
users.saveAndFlush(new User("user@example.com")))
.isInstanceOf(org.springframework.dao.DataIntegrityViolationException.class);
}
}
// Production migration contains:
// create unique index ux_users_email_lower on app_user (lower(email));
The test performs two inserts and one indexed uniqueness check. Container startup dominates runtime, so reuse one container per class or suite while keeping database state isolated.
flush, the exception may occur after the assertion or at transaction completion.exists check races with another transaction unless the database owns uniqueness.Spot a weak answer, missing edge case, or clearer explanation? Send it in.
Say thanks with a standalone, one-time $5 contribution. No account required.