name: testing description: Guidance for writing high-quality unit tests using Vladimir Khorikov's principles from Unit Testing. Use when (1) writing new tests, (2) refactoring existing tests, (3) reviewing test code, or (4) deciding what to test.
Testing - Khorikov's Unit Testing Principles
The Four Pillars
Evaluate tests against these four criteria:
- Protection against regressions - How well does the test catch bugs?
- Resistance to refactoring - Will the test survive implementation changes?
- Fast feedback - How quickly does the test run?
- Maintainability - How easy is the test to understand and modify?
Prioritize based on context. For legacy code, focus on regression protection. For new code, balance all four.
What to Test
Test Behavior, Not Implementation
- Verify observable outcomes, not internal state
- Tests should pass if behavior is correct, regardless of how it's implemented
- Ask: "Can a non-developer understand what this test verifies?"
The Domain Matters
- Business-critical logic: thorough testing
- Glue code: minimal testing
- Third-party libraries: no testing
Test Structure
AAA Pattern
// Arrange: Set up dependencies and inputs
// Act: Execute the behavior
// Assert: Verify the outcome
One Assertion Per Test (Prefer)
Multiple assertions are acceptable when they verify a single logical outcome.
Test Naming
Describe the scenario and expected behavior:
should_return_empty_list_when_no_items_existshould_throw_error_when_user_not_found
Mocking Guidelines
Only Mock What You Own
- Mock shared dependencies (databases, external services)
- Don't mock your own code under test
- Avoid mocking value objects or entities
The Principle
Mock roles, not objects. Tests should verify collaboration, not implementation.
Test Ethics
Delete Bad Tests
Remove tests that:
- Frequently produce false positives
- Are expensive to maintain
- Don't detect real bugs
False Positives Are Worse Than No Tests
A test that breaks during refactoring (when nothing is wrong) erodes trust and gets disabled.
Decision Framework
| Scenario | Approach |
|---|---|
| New feature | Write tests alongside implementation |
| Bug fix | Add regression test |
| Refactoring | Keep tests that verify behavior |
| Legacy code | Add tests when understanding, not before |
Code Review Checklist
- Does the test verify behavior, not implementation?
- Is the test name descriptive?
- Are dependencies minimal?
- Would this test survive a reasonable refactor?
- Does it provide clear value if it fails?