# Testing

> BDD-style test writing rules including unit testing patterns, integration testing with Testcontainers, contract testing for microservices (Pact, Spring Cloud Contract), E2E testing (Playwright, Cypress, visual regression), and performance testing strategies (k6, Gatling). Use when writing or modifying test code.

- Skill: `iceflower/testing` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds@latest add iceflower/testing`
- Raw SKILL.md: https://api.skillmd.com/api/skills/iceflower/testing/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- License: MIT
- Author: iceflower (https://skillmd.com/u/iceflower)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/iceflower/testing

---

# BDD-Style Unit Test Rules

## 1. BDD Style Required

- Always use BDD style (Describe - Context - It)
- Use English naming for test blocks with Korean descriptions

## 2. Naming Conventions

| Block | Pattern | Example |
| --- | --- | --- |
| `describe` (class) | `{ClassName} 클래스의` | `AuthFacade 클래스의` |
| `describe` (method) | `{methodName}() 메서드는` | `login() 메서드는` |
| `context` | `~하면`, `~이면` | `유효한 토큰이 주어지면` |
| `it` | `~한다`, `~반환한다` | `사용자 정보를 반환한다` |

---

## 3. Qualities of a Good Unit Test

Every test must satisfy all five criteria:

- **Accurate damage detection**: Tests MUST fail when code is broken. A passing test must provide confidence that the code works correctly
- **Implementation-independent**: Tests MUST still pass after refactoring internals. Test the public API behavior, not implementation details
- **Well-explained failure**: Failure messages alone must be sufficient to identify the problem
- **Readable test code**: Tests serve as documentation. Other developers must understand the intent immediately
- **Fast execution**: Unit tests must complete quickly since they are run frequently

---

## 4. Test Behaviors, Not Functions

- ❌ Do NOT mechanically create one test case per function
- ✅ Write separate test cases for each behavior a function performs
- A single function may exhibit different behaviors under different conditions — test each behavior individually
- NEVER skip error scenarios — test invalid inputs, boundary values, and exception cases alongside happy paths

---

## 5. Test One Behavior at a Time

- Testing multiple behaviors in a single test case makes failure diagnosis difficult
- ❌ Bundling multiple behaviors into one test
- ✅ Each test case verifies exactly one behavior independently
- Test case names must clearly describe the specific behavior being verified

---

## 6. Test Through Public APIs

- NEVER change private functions to public just for testing
- Test private function behavior indirectly through the public API
- Directly testing private functions couples tests to implementation — refactoring breaks tests even when behavior is unchanged
- If code is too complex to test through its public API, consider splitting it into smaller units

---

## 7. Test Double Guidelines

### Mock and Stub

- Mocks and stubs simplify tests by replacing real dependencies, but risk coupling to implementation details
- Tests using mocks/stubs may break during refactoring even when behavior is unchanged

### Fake

- Fakes are simplified implementations of real dependencies, providing more realistic tests than mocks/stubs
- Fakes decouple tests from implementation details
- Trade-off: fakes require their own maintenance

### Selection Criteria

| Situation | Recommended |
| --- | --- |
| Isolating from external systems (DB, API) | Fake or mock |
| Only need to control return values | Stub |
| Need to verify call count/args/order | Mock |
| Real dependency is lightweight and side-effect-free | Use the real dependency |

---

## 8. Shared Fixture Usage

- **State sharing (BeforeAll)**: Runs once before all test cases. Suitable for expensive dependencies, but shared mutable state between test cases can cause problems
- **Config sharing (BeforeEach)**: Runs before each test case. Guarantees isolation between test cases
- Mutable state MUST be reset via BeforeEach — sharing mutable state between test cases causes flaky tests
- Important setup values must be visible within each test case — hiding them in shared fixtures obscures test intent
- Shared constants are acceptable, but critical input values must be explicit in each test case

---

## 9. Use Appropriate Assertion Matchers

- Failure messages alone must be sufficient to diagnose the issue
- ❌ Poor assertion: failure shows only `Expected: true, But was: false` — no context
- ✅ Good assertion: failure clearly shows the difference between expected and actual values
- For collection assertions where order doesn't matter, use flexible matchers like `containsExactlyInAnyOrder`
- Leverage framework-provided matchers (e.g., AssertJ `containsAtLeast`, Jasmine `arrayContaining`)

---

## 10. Use Dependency Injection for Testability

- Hard-coded dependencies can make testing impossible
- Constructor injection allows replacing dependencies with fakes or mocks during tests
- Dependency injection improves not only testability but also modularity and flexibility
- Combine static factory methods with constructor injection to maintain both production convenience and testability

## 11. Anti-Patterns

- **Deploying Without Tests**: Deploying to production without test coverage is risky. At minimum, test core business logic
- **Testing Implementation Details**: Tests coupled to internal implementation break on refactoring. Write behavior-based tests
- **Inter-test Dependencies**: Tests that depend on execution order create unstable test suites. Each test must be independent
- **Excessive Mocking**: Mocking all dependencies diverges from real behavior. Supplement with integration tests
- **Magic Numbers**: Using meaningless numbers/strings in test data. Use intention-revealing constants or factories
- **Ignoring Flaky Tests**: Tolerating intermittently failing tests erodes overall test suite reliability. Fix or quarantine immediately

## Additional References

- For integration testing patterns, see [references/integration.md](references/integration.md)
- For contract testing patterns, see [references/contract.md](references/contract.md)
- For performance testing strategies, see [references/performance.md](references/performance.md)
- For E2E testing patterns (Playwright, Cypress, visual regression), see [references/e2e.md](references/e2e.md)

## Related Skills

- For load testing with k6 and Gatling, see `load-testing` skill

