Agent Skill
2/7/2026test-strategy
Test pyramid, testing strategy, and when to use which test type
O
objective
0GitHub Stars
1Views
npx skills add Objective-Arts/lens
SKILL.md
| Name | test-strategy |
| Description | Test pyramid, testing strategy, and when to use which test type |
name: test-strategy description: "Test pyramid, testing strategy, and when to use which test type"
Martin Fowler - Testing Strategy
Apply Martin Fowler's testing philosophy for balanced, effective test suites.
Core Philosophy
The Test Pyramid
/\
/ \ E2E / UI Tests
/ \ (few, slow, brittle)
/──────\
/ \ Integration Tests
/ \ (some, moderate speed)
/────────────\
/ \ Unit Tests
/ \ (many, fast, stable)
/──────────────────\
Key Insight: More tests at the bottom, fewer at the top.
Why a Pyramid?
| Level | Speed | Cost to Write | Cost to Maintain | Confidence |
|---|---|---|---|---|
| Unit | Fast (ms) | Low | Low | Component works |
| Integration | Medium (s) | Medium | Medium | Components work together |
| E2E | Slow (min) | High | High | System works for user |
The Anti-Pattern: Ice Cream Cone
──────────────
/ \ Manual Testing
/────────────────\
/ \ E2E Tests
/────────────────────\
/ \ Integration
──────────────────────── Unit (few)
Problem: Slow feedback, high maintenance, flaky tests.
Test Types Defined
Unit Tests
Test a single unit (class, function) in isolation.
// Unit test - no external dependencies
@Test
void calculateDiscount_goldCustomer_returns15Percent() {
DiscountCalculator calc = new DiscountCalculator();
double discount = calc.calculate(100.0, CustomerType.GOLD);
assertEquals(15.0, discount, 0.01);
}
Characteristics:
- Fast (milliseconds)
- No I/O (database, network, filesystem)
- Test one thing
- Use test doubles for dependencies
Integration Tests
Test how components work together.
// Integration test - tests real database interaction
@Test
void userRepository_savesAndRetrieves() {
UserRepository repo = new UserRepository(testDatabase);
User user = new User("alice@test.com");
repo.save(user);
User found = repo.findByEmail("alice@test.com");
assertEquals("alice@test.com", found.getEmail());
}
Characteristics:
- Slower (seconds)
- Tests real interactions
- May use test containers or in-memory databases
- Tests component boundaries
E2E Tests
Test complete user journeys through the system.
// E2E test - tests full user flow
@Test
void userCanCompleteCheckout() {
browser.navigateTo("/products");
browser.click("#add-to-cart-widget-1");
browser.click("#checkout");
browser.fillForm("#payment-form", validCard);
browser.click("#submit-order");
assertThat(browser.getCurrentUrl()).contains("/order-confirmation");
assertThat(browser.getText("#confirmation")).contains("Order placed");
}
Characteristics:
- Slow (minutes)
- Tests real user scenarios
- Most likely to be flaky
- Catches integration issues unit tests miss
When to Use Which
Use Unit Tests For
- Business logic - calculations, validations, transformations
- Edge cases - null handling, boundary conditions
- Algorithms - sorting, searching, parsing
- Fast feedback - run on every save
// Good unit test candidates
class PriceCalculator { /* pure logic */ }
class EmailValidator { /* validation rules */ }
class DateFormatter { /* transformations */ }
Use Integration Tests For
- Database access - repositories, queries
- External services - APIs, message queues
- Component interactions - service layer calling repository
- Configuration - Spring wiring, dependency injection
// Good integration test candidates
class UserRepository { /* database access */ }
class PaymentGateway { /* external API */ }
class OrderService { /* orchestrates multiple components */ }
Use E2E Tests For
- Critical user journeys - checkout, login, signup
- Smoke tests - "does the app start?"
- Contract verification - does the UI match the API?
- Regression for complex flows - multi-step wizards
// Good E2E test candidates
- Complete checkout flow
- User registration and login
- Search and filter results
- Critical business workflows
Practical Guidelines
The 70/20/10 Rule (approximate)
- 70% Unit Tests - Fast, focused, many
- 20% Integration Tests - Key boundaries
- 10% E2E Tests - Critical paths only
Test at the Right Level
WRONG: E2E test for validation logic
────────────────────────────────────
browser.fillInput("#email", "invalid");
browser.click("#submit");
expect(browser.getText(".error")).toBe("Invalid email");
// Slow, brittle, overkill for simple validation
RIGHT: Unit test for validation logic
────────────────────────────────────
@Test
void validate_invalidEmail_returnsFalse() {
assertFalse(EmailValidator.isValid("invalid"));
}
// Fast, focused, easy to maintain
Don't Duplicate Coverage
If unit tests cover the logic ──► Don't repeat in integration
If integration tests cover the flow ──► Don't repeat in E2E
The Sociable vs Solitary Unit Test
Solitary (London School): Mock all collaborators
@Test
void orderService_appliesDiscount() {
DiscountService mockDiscount = mock(DiscountService.class);
when(mockDiscount.calculate(any())).thenReturn(10.0);
OrderService orders = new OrderService(mockDiscount);
Order order = orders.create(items);
assertEquals(90.0, order.getTotal());
}
Sociable (Detroit School): Use real collaborators when simple
@Test
void orderService_appliesDiscount() {
DiscountService realDiscount = new DiscountService(); // Simple, no I/O
OrderService orders = new OrderService(realDiscount);
Order order = orders.create(items);
assertEquals(90.0, order.getTotal());
}
Fowler's View: Both are valid. Use solitary when collaborators are complex or slow. Use sociable when collaborators are simple and fast.
Test Isolation Strategies
Database Tests
// Option 1: In-memory database (fast)
@BeforeEach
void setUp() {
database = new H2Database(); // In-memory
}
// Option 2: Test containers (realistic)
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>();
// Option 3: Transaction rollback
@Transactional
@Test
void test() {
// Changes rolled back after test
}
External Service Tests
// Option 1: Mock the client
PaymentClient mockClient = mock(PaymentClient.class);
// Option 2: Fake server (WireMock, MockServer)
@BeforeEach
void setUp() {
stubFor(post("/charge")
.willReturn(ok().withBody("{\"status\":\"success\"}")));
}
// Option 3: Contract tests (Pact)
@Pact(consumer = "OrderService")
RequestResponsePact chargeCard(PactDslWithProvider builder) {
return builder
.given("valid card")
.uponReceiving("charge request")
.path("/charge")
.method("POST")
.willRespondWith()
.status(200)
.build();
}
Continuous Integration Strategy
Fast Feedback Loop
On every commit:
├── Unit tests (< 5 min)
└── Fast integration tests (< 10 min)
On PR/merge:
├── Full integration tests (< 30 min)
└── E2E smoke tests (< 15 min)
Nightly:
└── Full E2E suite (hours OK)
Test Selection
# Quick feedback during development
mvn test -Dgroups=unit
# Before push
mvn test -Dgroups=unit,integration
# Full suite
mvn test
Code Review Checklist
Test Level
- Logic tested at unit level?
- Boundaries tested at integration level?
- Critical paths tested at E2E level?
- No duplicate coverage across levels?
Test Quality
- Tests are fast enough for their level?
- Tests are deterministic (not flaky)?
- Tests are independent (no shared state)?
- Test names describe the behavior?
Coverage
- Happy path covered?
- Error cases covered?
- Edge cases covered at unit level?
- Key user journeys covered at E2E level?
Quick Reference
| Question | Answer |
|---|---|
| How to test business logic? | Unit test |
| How to test database queries? | Integration test |
| How to test API contracts? | Integration test with mocks |
| How to test user flows? | E2E test (sparingly) |
| How to test validation? | Unit test |
| How to test error handling? | Unit + integration |
| Tests too slow? | Move down the pyramid |
| Tests too brittle? | Move down the pyramid |
| Missing bugs in production? | Add integration tests |
Key Principles
- Write tests at the lowest level that gives confidence
- Unit tests for logic, integration tests for boundaries
- E2E tests are expensive - use sparingly for critical paths
- Fast tests run more often, catch bugs earlier
- Flaky tests destroy trust - fix or delete them
Resources
Skills Info
Original Name:test-strategyAuthor:objective
Download