Test Pyramid
Purpose: Define the target distribution of unit, integration, and system tests in StockEase.
The Test Pyramid Concept
Traditional Pyramid
Slow, expensive
Simulate user scenarios"] B[Integration 25%
Medium speed
Test layer interactions] C[Unit 70%
Fast, cheap
Test one thing] C --- B B --- A style A fill:#ffcdd2,stroke:#d32f2f,stroke-width:3px style B fill:#fff3e0,stroke:#f57c00,stroke-width:3px style C fill:#c8e6c9,stroke:#388e3c,stroke-width:3px
Benefits of This Shape
| Level | Tests | Speed | Cost | Benefit |
|---|---|---|---|---|
| Unit | Many | Fast (ms) | Low | Fast feedback during development |
| Integration | Some | Medium (sec) | Medium | Test layer interactions |
| System | Few | Slow (min) | High | Test real user scenarios |
StockEase Test Pyramid
Current Distribution
(Red - not implemented)"] B["Integration - 1 test 5%
@SpringBootTest context load (Green - complete)"] C["Unit Slice - 8 tests 95%
@WebMvcTest + unit tests (Green - complete)"] C --- B B --- A style A fill:#ffcdd2,stroke:#d32f2f,stroke-width:3px style B fill:#fff3e0,stroke:#388e3c,stroke-width:3px style C fill:#c8e6c9,stroke:#388e3c,stroke-width:3px
By Test Type
| Level | Count | Pattern | Files |
|---|---|---|---|
| Unit | 1 | Plain Mockito (no Spring) | AuthControllerTest |
| Slice | 7 | @WebMvcTest | Product*ControllerTest |
| Integration | 1 | @SpringBootTest | StockEaseApplicationTests |
| System/E2E | 0 | (Future) | - |
| TOTAL | 9 |
Rationale: Why This Distribution
β Why 70% Unit Tests
- Fast Feedback: Run entire suite in < 30 seconds
- Cost-Effective: No database, no server startup
- Development Friendly: Can run locally before commit
- Isolation: Test one component at a time
Example: AuthControllerTest β tests login logic without Spring context
β Why 25% Slice Tests
- Layer Coverage: Test controllers with HTTP layer
- Real HTTP Simulation: MockMvc simulates browser requests
- Authorization Testing: Verify security rules at HTTP level
- Fast Enough: Still complete < 1 sec per test
Examples: ProductFetchControllerTest, ProductCreateControllerTest
β³ Why 5% Integration Tests (Future)
- Verify Bean Wiring: Ensure Spring beans are connected
- Cross-Layer Flows: Test Service β Repository chains
- Configuration Validation: Check properties are loaded
- Slower: Worth it for critical paths
Current: StockEaseApplicationTests just checks context loads
β Why Not 100% System/E2E Tests
- Too Slow: Each test takes 10-30 seconds
- Too Expensive: Requires UI framework (Selenium, Playwright)
- Too Flaky: Browser automation has timing issues
- Not Scalable: Canβt run full suite before every commit
Future: Could add browser tests for critical user flows
Test Level Definitions
Level 1: Unit Tests (70%)
What they test: Single class or
method in isolation
Dependencies: All mocked
Framework: JUnit 5, Mockito
Speed: < 1 second
Example:
class AuthControllerTest {
@Mock AuthenticationManager authenticationManager;
@InjectMocks AuthController authController;
@Test
void testLoginSuccess() {
// Test just the login method, no HTTP
ResponseEntity response = authController.login(request);
assertThat(response.getStatusCode()).isEqualTo(OK);
}
}Level 2: Slice Tests (25%)
What they test: One horizontal layer
(controllers, repositories)
Dependencies: Layer tested is real,
others mocked
Framework: Spring Boot Test (@WebMvcTest, @DataJpaTest)
Speed: 0.5-2 seconds
Example:
@WebMvcTest(ProductController.class)
class ProductControllerTest {
@Autowired MockMvc mockMvc;
@MockitoBean ProductRepository repository; // Mocked
@Test
void testGetProducts() throws Exception {
// Test HTTP layer with real controller, mocked repo
mockMvc.perform(get("/api/products"))
.andExpect(status().isOk());
}
}Level 3: Integration Tests (5%)
What they test: Multiple layers
working together
Dependencies: All real (or most)
Framework: @SpringBootTest, full
Spring context
Speed: 2-5 seconds
Example:
@SpringBootTest
class StockEaseApplicationTests {
@Test
void contextLoads() {
// Test: entire app boots, all beans wire correctly
}
}Level 4: System/E2E Tests (0% - Future)
What they test: Complete user
scenarios end-to-end
Scope: Browser β Frontend β Backend β
Database
Framework: Playwright, Cypress,
Selenium
Speed: 10-30 seconds per test
Example (future):
test('User can login and create product', async () => {
// Visit login page
await page.goto('http://localhost:3000/login');
// Fill form
await page.fill('input[name="username"]', 'admin');
// Submit
await page.click('button[type="submit"]');
// Verify redirect
await page.waitForNavigation();
// Create product
await page.click('button:has-text("New Product")');
});Current Test Breakdown by Feature
Authentication
| Test | Type | Coverage |
|---|---|---|
testLoginSuccess |
Unit | β Successful login with JWT |
testAdminLoginSuccess |
Unit | β Admin role login |
testLoginFailureWithInvalidCredentials |
Unit | β Wrong password |
testLoginFailureWithUserNotFound |
Unit | β Unknown user |
Total: 4 tests, 100% unit
Product Fetch
| Test | Type | Coverage |
|---|---|---|
testGetProductsSuccess |
Slice | β Get all products |
testGetProductByIdSuccess |
Slice | β Get by ID |
testGetProductByIdNotFound |
Slice | β Product not found |
testLowStockProductsWithRoles |
Slice | β Low stock (parameterized) |
Total: 4-5 tests, 100% slice
Product Create
| Test | Type | Coverage |
|---|---|---|
testValidProductCreation |
Slice | β Admin creates product |
testProductCreationDeniedForUser |
Slice | β User blocked (403) |
testProductCreationWithoutAuth |
Slice | β Unauthenticated (401) |
Total: 3 tests, 100% slice
Product Update
| Test | Type | Coverage |
|---|---|---|
testValidProductUpdate |
Slice | β Admin updates |
testInvalidUpdateQuantity |
Slice | β Negative quantity validation |
testProductUpdateDeniedForUser |
Slice | β User blocked (403) |
Total: 3 tests, 100% slice
Product Delete
| Test | Type | Coverage |
|---|---|---|
testDeleteProductSuccess |
Slice | β Admin deletes |
testDeleteProductDeniedForUser |
Slice | β User blocked (403) |
Total: 2 tests, 100% slice
Product Pagination
| Test | Type | Coverage |
|---|---|---|
testPaginationWithValidParams |
Slice | β Valid page/size |
testPaginationWithInvalidParams |
Slice | β Invalid parameters |
Total: 2 tests, 100% slice
Application Bootstrap
| Test | Type | Coverage |
|---|---|---|
contextLoads |
Integration | β Spring context loads |
Total: 1 test, 100% integration
Recommended Test Additions
To Improve Coverage (Future)
| Layer | Current | Recommended | Benefit |
|---|---|---|---|
| Service | 0 | 3-5 unit tests | Test business logic isolation |
| Repository | 0 | 2-3 @DataJpaTest | Test query methods |
| Security | 7 (in controllers) | 2 dedicated | Focused security tests |
| Validation | 1 test | 3-5 unit tests | Input validation rules |
| System/E2E | 0 | 1-2 Playwright | Critical user flows |
Service Layer Tests (Proposed)
@ExtendWith(MockitoExtension.class)
class ProductServiceTest {
@Mock
private ProductRepository repository;
@InjectMocks
private ProductService service;
@Test
void testCreateProduct() {
// Test: Service calls repository, returns product
}
@Test
void testUpdateProductQuantity() {
// Test: Quantity calculation and updates
}
}Maintaining the Pyramid
When Adding Tests
- First try: Can it be a unit test? (fastest)
- Then: Does it need HTTP? β Slice test
- Last: Does it need multiple layers? β Integration test
- Rarely: Does it need UI? β E2E test
Anti-Patterns to Avoid
β Inverted Pyramid (Too many integration/E2E tests)
βΌ
β± β²
β± β² Many slow tests
β±ββββββ² Few fast tests
β± β² β²
β± β² β² Bad!
β Hourglass (Skip middle tests)
βΌ
β± β²
β± β² Many units, few slices
β±ββββββ² Many E2E, few integration
β± β²
β± β² Bad!
β Healthy Pyramid (What we have)
βΌ
β± β²
β± β² Lots of fast unit tests
β±ββββββ² Some medium slice tests
β± β² Few slow integration tests
β± β² Good!
Metrics to Track
Execution Speed
# Current times (from mvn test output)
Unit Tests: < 1 sec
Slice Tests: 2-3 sec
Integration: 1-2 sec
Total Suite: < 30 sec
# Target
Unit Tests: < 1 sec per 10 tests
Slice Tests: < 2 sec per test
Integration: < 5 sec per test
Total Suite: < 60 secCoverage by Level
Unit & Slice Tests: ~65-70% line coverage
Estimated by Layers:
- Controllers: 95% (heavily tested)
- Services: 0% (mocked)
- Repositories: 0% (mocked)
Related Documentation
Testing Fundamentals
- Testing Strategy β Goals and philosophy
- Coverage & Quality β Coverage metrics and JaCoCo
- Naming Conventions β Test method names
Implementation
- Spring Slices β @WebMvcTest, @DataJpaTest patterns
- Controller Integration Tests β MockMvc examples
Main Architecture
- Testing Architecture β Entry point
- Backend Architecture β Components being tested
Last Updated: October 31, 2025
Version: 1.0
Status: β
Reflects current StockEase
pyramid