β¬ οΈ 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
(~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 -DskipTestsIDE 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
- Test Fixtures & Data Builders - Helper patterns, OAuth2 setup, test data builders
- Unit Testing Patterns - Component isolation, mocking, test organization
- Integration Testing - @DataJpaTest, @WebMvcTest, database testing
- Security Testing - OAuth2, RBAC, API authentication tests
- Test Coverage (JaCoCo) - Report location, plugin configuration, suggested targets
Related Documentation
- Validation Framework - How validation is tested
- Exception Handling - Error scenario testing
- Architecture Overview - System design context
- Custom Validators - Validator test examples