⬅️ Back to Architecture Index

Testing Strategy & Patterns

Overview

Smart Supply Pro implements a comprehensive, layered testing strategy using industry-standard tools and patterns. The test suite covers:

  • Unit Tests - Individual components in isolation (mappers, validators, services, repositories, enum helpers)
  • Integration Tests - Component interaction with databases and frameworks
  • Security Tests - OAuth2, authorization, RBAC, and API endpoints
  • Analytics Tests - Complex queries, converters, and reporting
  • Contract Tests - REST API endpoints, HTTP status codes, response formats

The project uses: - JUnit 5 - Test framework with parameterized tests and display names - Mockito - Mocking and behavior verification - Spring Test - Integration testing with @SpringBootTest, @DataJpaTest, @WebMvcTest - TestContainers - Database containerization for realistic testing - H2 Database - In-memory database for fast unit tests - JaCoCo - Code coverage reporting


Testing Pyramid

graph TB E2E["πŸ”οΈ E2E / Integration Tests
(~10%)
@SpringBootTest
Full context, real DB/cache"] Integration["πŸ“Š Integration Tests
(~25%)
@DataJpaTest, @WebMvcTest
Slice testing with mocks"] Unit["πŸ“ Unit Tests
(~65%)
@ExtendWith(MockitoExtension)
Fast, isolated components"] Unit --> Integration Integration --> E2E style Unit fill:#c8e6c9 style Integration fill:#fff9c4 style E2E fill:#ffcdd2

Test Layers

Layer 1: Unit Tests (65% of suite)

Scope: Test individual classes in isolation with mocked dependencies

Tools: - JUnit 5 with @Test and @ParameterizedTest - Mockito with @ExtendWith(MockitoExtension.class) - @Mock for dependencies, @InjectMocks for target class

Examples: - Validator tests (InventoryItemValidatorTest, InventoryItemValidatorBusinessRulesTest, SupplierValidatorTest, SupplierValidatorUniquenessAndDeletionEdgeCasesTest, StockHistoryValidationTest, StockHistoryValidatorPriceChangeAndEnumValidationTest, InventoryItemSecurityValidatorTest) - Service method tests with mocked repositories - Converter tests (AnalyticsServiceImplConverterTest, AnalyticsConverterHelperTest) - Exception tests (e.g., GlobalExceptionHandlerTest, BusinessExceptionHandlerTest)

Characteristics: - βœ… Fast (milliseconds per test) - βœ… No database or Spring context needed - βœ… Easy to understand and maintain - βœ… Fail quickly for logic errors

@ExtendWith(MockitoExtension.class)
class InventoryItemValidatorTest {
    
    @Mock private InventoryItemRepository repo;
    
    @Test
    void validateDuplicate_NameAndPriceExist_ThrowsConflict() {
        when(repo.findByNameIgnoreCase("Widget"))
            .thenReturn(List.of(existingItem));
        
        assertThrows(DuplicateResourceException.class,
            () -> InventoryItemValidator.validateInventoryItemNotExists(
                "Widget", price, repo));
    }
}

Layer 2: Integration Tests (25% of suite)

Scope: Test component interactions with real infrastructure (DB, frameworks)

Tools: - @DataJpaTest - JPA repository testing with H2 - @WebMvcTest - Controller testing without full context - @SpringBootTest - Full application context for complex scenarios - MockMvc for HTTP testing - @MockitoBean for selective mocking

Examples: - Repository tests (SupplierRepositoryTest, InventoryItemRepositoryTest) - Controller tests (SupplierControllerTest) - Service integration tests

Characteristics: - ⏱️ Slower (100ms - 1s per test) - πŸ“¦ Uses real H2 database or TestContainers - πŸ”— Tests component interaction - πŸ›‘οΈ Validates persistence and HTTP contracts

@DataJpaTest
@AutoConfigureTestDatabase(replace = ANY)  // force H2
class SupplierRepositoryTest {
    
    @Autowired private SupplierRepository repo;
    
    @Test
    void findByNameIgnoreCase_caseInsensitive() {
        repo.save(Supplier.builder().name("Acme GmbH").build());
        
        assertTrue(repo.findByNameIgnoreCase("acme gmbh").isPresent());
    }
}

Layer 3: End-to-End Tests (10% of suite)

Scope: Full application in realistic conditions

Tools: - @SpringBootTest with real security - TestContainers for Oracle or real services - MockMvc or RestTemplate for HTTP - Full authentication/authorization

Examples: - Security smoke tests (SecuritySmokeTest) - Demo mode tests (DemoReadonlySecurityTest) - Full workflow tests

Characteristics: - 🐒 Slowest (seconds per test) - πŸ” Tests with real security context - 🌍 Validates end-to-end workflows - 🎯 High confidence, lower coverage


Test Organization

Directory Structure

src/test/java/com/smartsupplypro/inventory/
β”œβ”€β”€ InventoryServiceApplicationTest.java          (Context load smoke test)
β”œβ”€β”€ InventoryServiceApplicationMainTest.java      (Entry-point coverage: invokes main() and closes immediately)
β”‚
β”œβ”€β”€ enums/
β”‚   └── StockChangeReasonTest.java                (Unit: enum helper methods + parsing)
β”‚
β”œβ”€β”€ mapper/
β”‚   β”œβ”€β”€ InventoryItemMapperTest.java              (Unit: entity/DTO mapping + computed fields)
β”‚   β”œβ”€β”€ StockHistoryMapperTest.java               (Unit: audit mapping + enum/string conversion)
β”‚   └── SupplierMapperTest.java                   (Unit: sanitization + audit field handling)
β”‚
β”œβ”€β”€ model/
β”‚   β”œβ”€β”€ InventoryItemTest.java                    (Unit: entity @PrePersist defaults + supplierId resolution)
β”‚   └── StockHistoryTest.java                     (Unit: entity @PrePersist timestamp defaulting)
β”‚
β”œβ”€β”€ validation/
β”‚   β”œβ”€β”€ InventoryItemValidatorTest.java           (Unit: 159 lines)
β”‚   β”œβ”€β”€ InventoryItemValidatorBusinessRulesTest.java (Unit: incremental business rule branches)
β”‚   β”œβ”€β”€ InventoryItemSecurityValidatorTest.java    (Unit: RBAC/field-level update guards)
β”‚   β”œβ”€β”€ SupplierValidatorTest.java                (Unit: ~100 lines)
β”‚   β”œβ”€β”€ SupplierValidatorUniquenessAndDeletionEdgeCasesTest.java (Unit: uniqueness + deletion edge cases)
β”‚   β”œβ”€β”€ StockHistoryValidationTest.java           (Unit: ~100 lines)
β”‚   └── StockHistoryValidatorPriceChangeAndEnumValidationTest.java (Unit: PRICE_CHANGE + enum whitelist)
β”‚
β”œβ”€β”€ service/
β”‚   β”œβ”€β”€ SecurityServiceTest.java                  (Unit: demo-mode principal checks)
β”‚   β”œβ”€β”€ CustomUserServiceTestSupport.java         (Shared fixture: upstream principals + service factories)
β”‚   β”œβ”€β”€ CustomUserServiceNormalizationTest.java   (Unit: allow-list parsing + role authority normalization)
β”‚   β”œβ”€β”€ CustomOAuth2UserServiceTest.java          (Unit: OAuth2 provisioning + error handling)
β”‚   β”œβ”€β”€ CustomOAuth2UserServiceRoleHealingTest.java (Unit: role healing + idempotency)
β”‚   β”œβ”€β”€ CustomOidcUserServiceTest.java            (Unit: OIDC provisioning + error handling)
β”‚   β”œβ”€β”€ CustomOidcUserServiceRoleHealingTest.java (Unit: role healing + idempotency)
β”‚   β”œβ”€β”€ inventoryitem/
β”‚   β”‚   β”œβ”€β”€ InventoryItemServiceSaveHappyPathTest.java (Unit: save happy paths + audit logging)
β”‚   β”‚   β”œβ”€β”€ InventoryItemServiceSaveValidationTest.java (Unit: save validation guards)
β”‚   β”‚   └── InventoryItemServiceSaveTestBase.java      (Shared fixture)
β”‚   β”œβ”€β”€ supplier/
β”‚   β”‚   β”œβ”€β”€ SupplierServiceCreateTest.java            (Unit: create flows)
β”‚   β”‚   β”œβ”€β”€ SupplierServiceUpdateTest.java            (Unit: update flows)
β”‚   β”‚   β”œβ”€β”€ SupplierServiceDeleteTest.java            (Unit: delete flows)
β”‚   β”‚   β”œβ”€β”€ SupplierServiceReadCountTest.java         (Unit: read/query/count flows)
β”‚   β”‚   └── SupplierServiceTestBase.java              (Shared fixture)
β”‚   β”œβ”€β”€ stockhistory/
β”‚   β”‚   β”œβ”€β”€ StockHistoryServiceLogTest.java           (Unit: audit logging + validation)
β”‚   β”‚   β”œβ”€β”€ StockHistoryServiceDeleteTest.java        (Unit: delete audit logging)
β”‚   β”‚   β”œβ”€β”€ StockHistoryServiceReadTest.java          (Unit: stock history queries)
β”‚   β”‚   β”œβ”€β”€ StockHistoryServiceSaveTest.java          (Unit: service-level save)
β”‚   β”‚   └── StockHistoryServiceTestBase.java          (Shared fixture)
β”‚   └── impl/
β”‚       β”œβ”€β”€ analytics/
β”‚       β”‚   β”œβ”€β”€ AnalyticsServiceImplTest.java
β”‚       β”‚   β”œβ”€β”€ AnalyticsServiceImplConverterTest.java (Unit: 150 lines)
β”‚       β”‚   β”œβ”€β”€ AnalyticsConverterHelperTest.java       (Unit: direct helper coverage)
β”‚       β”‚   β”œβ”€β”€ AnalyticsServiceImplQueryTest.java
β”‚       β”‚   β”œβ”€β”€ AnalyticsServiceImplValidationTest.java
β”‚       β”‚   β”œβ”€β”€ AnalyticsServiceImplWindowTest.java
β”‚       β”‚   β”œβ”€β”€ AnalyticsServiceImplWacTest.java
β”‚       β”‚   └── FinancialAnalyticsServiceWacBucketsTest.java (Unit: WAC branch coverage suite)
β”‚       β”œβ”€β”€ inventory/
β”‚       β”‚   β”œβ”€β”€ InventoryItemValidationHelperTest.java  (Unit: server-side validation + field population)
β”‚       β”‚   └── InventoryItemAuditHelperTest.java       (Unit: stock-history audit logging)
β”‚       └── inventoryitem/
β”‚           β”œβ”€β”€ InventoryItemServiceImplSaveTest.java (Unit: 120 lines)
β”‚           β”œβ”€β”€ InventoryItemServiceImplSearchTest.java
β”‚           β”œβ”€β”€ InventoryItemServiceImplUpdateTest.java
β”‚           β”œβ”€β”€ InventoryItemServiceImplDeleteTest.java
β”‚           β”œβ”€β”€ InventoryItemServiceImplReadAndAdjustmentsTest.java
β”‚           └── InventoryItemServiceImplTestHelper.java (Shared utilities)
β”‚
β”œβ”€β”€ repository/
β”‚   β”œβ”€β”€ SupplierRepositoryTest.java               (Integration: 126 lines)
β”‚   β”œβ”€β”€ InventoryItemRepositoryTest.java
β”‚   β”œβ”€β”€ AppUserRepositoryTest.java
β”‚   β”œβ”€β”€ StockHistoryRepositoryFilteringTest.java
β”‚   β”œβ”€β”€ StockHistoryRepositoryAnalyticsTest.java
β”‚   β”œβ”€β”€ InventoryItemRepositoryAnalyticsTest.java
β”‚   └── custom/
β”‚       β”œβ”€β”€ StockHistoryCustomRepositoryImplTest.java
β”‚       β”œβ”€β”€ StockMetricsRepositoryImplTest.java
β”‚       β”œβ”€β”€ StockDetailQueryRepositoryImplTest.java
β”‚       β”œβ”€β”€ StockTrendAnalyticsRepositoryImplH2Test.java
β”‚       β”œβ”€β”€ StockTrendAnalyticsRepositoryImplOracleDialectSelectionTest.java
β”‚       └── util/
β”‚           └── DatabaseDialectDetectorTest.java   (Unit: profile-based dialect detection branches)
β”‚
β”œβ”€β”€ controller/
β”‚   β”œβ”€β”€ supplier/
β”‚   β”‚   └── SupplierControllerTest.java           (Integration: 404 lines)
β”‚   β”œβ”€β”€ auth/
β”‚   β”‚   β”œβ”€β”€ AuthControllerTest.java               (Integration: @WebMvcTest /api/me)
β”‚   β”‚   β”œβ”€β”€ AuthControllerAuthoritiesTest.java    (Integration: @WebMvcTest /api/me/authorities)
β”‚   β”‚   β”œβ”€β”€ AuthControllerLogoutTest.java         (Integration: @WebMvcTest logout contract)
β”‚   β”‚   └── AuthControllerBranchUnitTest.java     (Unit: guard branches blocked by security filters)
β”‚   β”œβ”€β”€ health/
β”‚   β”‚   β”œβ”€β”€ HealthCheckControllerHealthEndpointTest.java (Unit: /api/health response contract)
β”‚   β”‚   └── HealthCheckControllerDbEndpointTest.java     (Unit: /api/health/db response contract)
β”‚   β”œβ”€β”€ inventoryitem/
β”‚   β”œβ”€β”€ stockhistory/
β”‚   β”‚   β”œβ”€β”€ StockHistoryControllerEndpointsTest.java (Integration: @WebMvcTest endpoint contract)
β”‚   β”‚   └── StockHistoryControllerSearchTest.java    (Integration: @WebMvcTest search validation + paging)
β”‚   β”œβ”€β”€ analytics/
β”‚   β”‚   β”œβ”€β”€ AnalyticsControllerBasicTest.java      (Integration: @WebMvcTest stock dashboard endpoints)
β”‚   β”‚   β”œβ”€β”€ AnalyticsControllerFilteringTest.java  (Integration: @WebMvcTest filtering/search endpoints)
β”‚   β”‚   β”œβ”€β”€ AnalyticsControllerFinancialTest.java  (Integration: @WebMvcTest financial analytics endpoints)
β”‚   β”‚   β”œβ”€β”€ AnalyticsControllerValidationHelperTest.java (Unit: request parameter validation rules)
β”‚   β”‚   └── AnalyticsDashboardHelperTest.java      (Unit: dashboard aggregation + supplier conditional branches)
β”‚   └── security/
β”‚
β”œβ”€β”€ exception/
β”‚   β”œβ”€β”€ InvalidRequestExceptionTest.java          (Unit: exception DTO/context branches)
β”‚   β”œβ”€β”€ DuplicateResourceExceptionTest.java       (Unit: message/context branches)
β”‚   β”œβ”€β”€ GlobalExceptionHandlerTest.java           (Unit: handler branch coverage)
β”‚   β”œβ”€β”€ BusinessExceptionHandlerTest.java         (Unit: handler branch coverage)
β”‚   └── ErrorResponseBuilderTest.java             (Unit: ErrorResponse.Builder fallback + generation branches)
β”‚
β”œβ”€β”€ security/
β”‚   β”œβ”€β”€ SecuritySmokeTest.java                    (Integration: 297 lines)
β”‚   β”œβ”€β”€ oauth2login/
β”‚   β”‚   β”œβ”€β”€ OAuth2LoginSuccessHandlerTestSupport.java
β”‚   β”‚   β”œβ”€β”€ OAuth2LoginSuccessHandlerProvisioningTest.java
β”‚   β”‚   β”œβ”€β”€ OAuth2LoginSuccessHandlerReturnCookieTest.java
β”‚   β”‚   └── OAuth2LoginSuccessHandlerCookieHeaderBuilderTest.java
β”‚   β”œβ”€β”€ ApiEntryPointBehaviourTest.java
β”‚   β”œβ”€β”€ DemoReadonlySecurityTest.java
β”‚   β”œβ”€β”€ SecurityConfigAuthorizationRulesTest.java  (Integration: @WebMvcTest real authZ rules)
β”‚   β”œβ”€β”€ SecurityConfigDemoReadonlyAuthorizationTest.java (Integration: demo-readonly branch)
β”‚   β”œβ”€β”€ oauth2/
β”‚   β”‚   β”œβ”€β”€ CookieOAuth2AuthorizationRequestRepositoryTestSupport.java
β”‚   β”‚   β”œβ”€β”€ CookieOAuth2AuthorizationRequestRepositoryTest.java
β”‚   β”‚   β”œβ”€β”€ CookieOAuth2AuthorizationRequestRepositoryLoadTest.java
β”‚   β”‚   β”œβ”€β”€ CookieOAuth2AuthorizationRequestRepositorySaveTest.java
β”‚   β”‚   β”œβ”€β”€ CookieOAuth2AuthorizationRequestRepositorySerializationTest.java
β”‚   β”‚   └── CookieOAuth2AuthorizationRequestRepositorySameSiteTest.java
β”‚   β”œβ”€β”€ TestApiStubController.java                (Probe endpoints)
β”‚   └── AdminStubController.java
β”‚
└── config/
    β”œβ”€β”€ TestSecurityConfig.java                    (Helper: simplified security for controller slices)
    β”œβ”€β”€ SecurityConfigUnitTest.java                (Unit: CORS/cookie/OAuth failure handler contracts)
    β”œβ”€β”€ SecurityFilterHelperTest.java              (Unit: API request classification filter)
    └── SecurityEntryPointHelperTest.java          (Unit: API 401 JSON vs web redirect entry points)

src/test/resources/
β”œβ”€β”€ logback-test.xml                              (Test logging: reduce console noise)
└── testcontainers.properties                     (TestContainers config)

Test Coverage

Coverage Summary

Note: entity lifecycle hooks (for example @PrePersist) should be covered via pure unit tests under src/test/java/com/smartsupplypro/inventory/model/.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Test Coverage (via JaCoCo Maven plugin)             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Target: >80% code coverage                          β”‚
β”‚ Report: target/site/jacoco/index.html              β”‚
β”‚ Execution: mvn clean verify                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Coverage by Layer:                                  β”‚
β”‚ - Validation:    95%+ (critical business logic)    β”‚
β”‚ - Service:       85%+ (core operations)            β”‚
β”‚ - Repository:    80%+ (custom queries)             β”‚
β”‚ - Controller:    75%+ (HTTP endpoints)             β”‚
β”‚ - Security:      85%+ (auth flows)                 β”‚
β”‚ - Config:        60%+ (framework wiring)           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

JaCoCo Configuration

<!-- pom.xml -->
<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.12</version>
    <executions>
        <execution>
            <goals><goal>prepare-agent</goal></goals>
        </execution>
        <execution>
            <id>report</id>
            <phase>verify</phase>
            <goals><goal>report</goal></goals>
        </execution>
    </executions>
</plugin>

Test Dependencies

Testing Stack (from pom.xml)

<!-- JUnit 5 & Mockito (via spring-boot-starter-test) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<!-- Includes: JUnit 5, Mockito, AssertJ, Hamcrest, JSONassert -->

<!-- Spring Test Utilities -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <scope>test</scope>
</dependency>

<!-- TestContainers for Database Isolation -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-testcontainers</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>oracle-free</artifactId>
    <scope>test</scope>
</dependency>

<!-- H2 for Unit Tests (in-memory, fast) -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>
</dependency>

Running Tests

Maven Commands

# Run all tests
mvn test

# Run tests with coverage report
mvn clean verify

# Run specific test class
mvn test -Dtest=SupplierRepositoryTest

# Run tests matching pattern
# Note: validator-related tests include both *Validator*Test and *Validation*Test suites.
mvn test -Dtest=*Validator*Test,*Validation*Test

# Run with detailed output
mvn test -X

# Skip tests during build
mvn clean package -DskipTests

IDE Integration

IntelliJ IDEA: - Right-click test class β†’ Run β€˜TestClassName’ - Right-click test method β†’ Run β€˜methodName()’ - Coverage: Run β†’ Run with Coverage

VS Code (with Extension Pack for Java): - CodeLens link above test methods - Click β€œRun Test” or β€œDebug Test”


Test Profiles

test Profile (application-test.yml)

  • Uses H2 in-memory database (Oracle compatibility mode)
  • Disables Testcontainers checks
  • Configures test-specific properties

Rationale: H2 is fast and reliable for repository and MVC slice tests, and it works in any CI environment. Running against the real Oracle Free Tier database in CI is intentionally avoided because:

  • Access typically requires an Oracle Wallet on the runner and correct client configuration.
  • Oracle Free Tier commonly enforces IP allowlisting; GitHub-hosted runners use dynamic egress IPs, so you cannot reliably pre-allowlist them without a dedicated VM, self-hosted runner, or tunneling.

If a test must validate Oracle-specific behavior, prefer: - TestContainers (local/dev environments with Docker), or - A controlled CI environment with a stable outbound IP (self-hosted runner/VM) where allowlisting is feasible.

For manual testing against Oracle Free Tier from a developer machine, use the opt-in oracle-it profile (wallet-based connectivity). It is intentionally disabled by default and only activates when you explicitly set ENABLE_WALLET_TEST=true for InventoryServiceApplicationTest.

# Spring profile for testing
spring.profiles.active=test

# H2 embedded database (Oracle mode)
spring.datasource.url=jdbc:h2:mem:testdb;MODE=Oracle
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

# Disable unnecessary services
spring.h2.console.enabled=false
management.endpoints.web.exposure.exclude=*

Test Fixtures & Helpers

Test Helper Classes

Class Purpose
InventoryItemServiceImplTestHelper OAuth2 authentication setup, test data builders
TestSecurityConfig Mock OAuth2 config for @WebMvcTest
TestApiStubController Probe endpoints for security testing
AdminStubController Admin-protected endpoints for RBAC tests

Common Patterns

// 1. Authentication setup in service tests
@BeforeEach
void setup() {
    InventoryItemServiceImplTestHelper.authenticateAsOAuth2("admin", "ADMIN");
}

// 2. Build test DTOs
private InventoryItemDTO validDTO() {
    return InventoryItemDTO.builder()
        .name("Widget")
        .quantity(100)
        .price(BigDecimal.TEN)
        .supplierId("SUPP-001")
        .build();
}

// 3. Controller testing with MockMvc
mockMvc.perform(post("/api/suppliers")
    .contentType(MediaType.APPLICATION_JSON)
    .content(objectMapper.writeValueAsString(dto))
    .with(user("admin").roles("ADMIN")))
    .andExpect(status().isCreated())
    .andExpect(jsonPath("$.id").exists());

TestContainers Configuration

testcontainers.properties

Location: src/test/resources/testcontainers.properties

Purpose: Configures TestContainers behavior during test execution.

Content:

checks.enabled=false

What This Does:

Property Value Meaning
checks.enabled false Disables startup checks and health verification for TestContainers

Why Disable Checks?

TestContainers normally performs startup verification checks before running tests: - Validates container health and readiness - Checks database connectivity - Verifies all dependencies are ready

Performance Impact: - βœ… Faster test startup - Skips checks, reduces ~2-5 second overhead per test run - βœ… Cleaner test output - No diagnostic logs from health checks - ⚠️ Trade-off - Fewer diagnostics if container fails to start (rare in practice)

How Testing Configuration Works:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Test Execution Flow                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1. Maven finds @ActiveProfiles("test")     β”‚
β”‚ 2. Loads application-test.yml              β”‚
β”‚ 3. Sees: spring.datasource.url = H2 mem:   β”‚
β”‚ 4. Creates H2 in-memory database           β”‚
β”‚ 5. Reads testcontainers.properties         β”‚
β”‚ 6. checks.enabled=false β†’ Skip health verifyβ”‚
β”‚ 7. Starts test execution                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Point: testcontainers.properties is a supplement to application-test.yml, not a replacement. The actual database configuration (H2, Oracle mode, connectivity) comes from application-test.yml. This file only tunes TestContainers behavior.

When to Modify testcontainers.properties

Rarely needed, but if you encounter TestContainers startup issues:

# Enable debugging if containers fail to start
checks.enabled=true

# For Oracle TestContainers specifically
testcontainers.docker.client.strategy=org.testcontainers.dockerclient.UnixSocketClientProviderStrategy

Default Configuration Sufficient for: - βœ… Unit tests with H2 (no containers used) - βœ… Integration tests with mocked services - βœ… CI/CD pipelines with standard Docker setup


Test Fixtures & Data Builders

Test Helper Classes

Class Purpose Location
InventoryItemServiceImplTestHelper OAuth2 authentication setup, principal creation src/test/.../service/impl/
TestSecurityConfig Mock OAuth2 config for @WebMvcTest src/test/.../config/
TestApiStubController Probe endpoints for security testing src/test/.../security/
AdminStubController Admin-protected endpoints for RBAC tests src/test/.../security/

See: Test Fixtures & Data Builders for detailed documentation on: - OAuth2 authentication helper patterns - Test data builders and factories - Test isolation and cleanup strategies - Best practices for reusable test code

Quick Example

// Authenticate test with OAuth2 principal
@Test
void testAdminCanSave() {
    InventoryItemServiceImplTestHelper.authenticateAsOAuth2("admin@example.com", "ADMIN");
    
    InventoryItem item = InventoryItem.builder()
            .id("item-1")
            .name("Widget")
            .price(BigDecimal.TEN)
            .quantity(100)
            .build();
    
    InventoryItem saved = service.save(item);
    assertNotNull(saved.getId());
}

Quick Navigation



⬅️ Back to Architecture Index