Integration Testing for Strong Senior Developers
What is the difference between unit tests and integration tests, and when
should you use each?
Unit tests:
Isolate and test a single component or method.
Use mocks/stubs for dependencies.
Fast and reliable, ideal for logic validation.
Integration tests:
Test interactions between multiple components or layers (e.g., DB, APIs, services).
Validate real configurations, real dependencies.
Slower but critical for detecting real-world issues.
Use both for comprehensive test coverage.
How do you set up database integration tests in Spring applications?
Use @DataJpaTest for testing JPA repositories with an in-memory DB.
Use @SpringBootTest with @AutoConfigureTestDatabase for full context integration.
Strategies:
Use H2 for lightweight testing or Testcontainers for realistic databases.
Reset schema with @Sql annotations or rollback via @Transactional.
Use Flyway or Liquibase for initializing test data.
How can you test REST API integrations effectively?
Use @SpringBootTest (webEnvironment = RANDOM_PORT) or @WebMvcTest for
controller-level testing.
Tools:
RestTemplate, WebTestClient, or MockMvc for test interactions.
Assertions:
Status codes, headers, JSON payloads (using JSONPath or AssertJ).
Avoid external dependencies — mock services using WireMock or MockWebServer.
How would you approach integration testing in a microservices environment?
Strategies:
Use contract testing (e.g., Pact) for consumer-provider validation.
Use Testcontainers or Docker Compose to run dependent services.
Use shared test libraries for common test contracts.
Best practices:
Automate integration tests as part of CI pipeline.
Tag long-running tests to separate from unit suite.
Monitor external API interactions and isolate flaky dependencies.
How do you manage test data setup and cleanup in integration tests?
Approaches:
Use @Sql or @SqlGroup to run schema/data setup scripts.
Use @Transactional + rollback for automatic cleanup.
Use @Testcontainers for isolated databases per test.
Seed data with repositories or custom test utilities.
Avoid shared mutable state to prevent test interference.
Ensure consistency across local and CI environments.