Testing Overview
Testing Overview
Section titled โTesting OverviewโAutomated testing gives you confidence that your code does what itโs supposed to do โ and keeps doing it as the codebase grows.
The Testing Pyramid
Section titled โThe Testing Pyramidโ /\ /E2E\ โ Few, slow, expensive /------\ / Integ \ โ Some /----------\ / Unit \ โ Many, fast, cheap /______________\| Level | Scope | Speed | Cost | Quantity |
|---|---|---|---|---|
| Unit | Single function / class | < 1ms | Low | Many |
| Integration | Multiple components together | Seconds | Medium | Some |
| End-to-End | Full user journey | Minutes | High | Few |
Test Types Explained
Section titled โTest Types ExplainedโUnit Tests
Section titled โUnit TestsโTest a single unit of code in isolation. External dependencies (databases, APIs, time) are mocked.
- Fast โ run in milliseconds
- Precise โ pinpoint exactly what broke
- Fragile to refactoring if testing implementation not behaviour
Integration Tests
Section titled โIntegration TestsโTest multiple components working together โ e.g., a service + database, or an API endpoint + controller + database layer.
- Slower than unit tests
- Catch issues that unit tests miss (misconfigured ORM, wrong SQL, mismatched interfaces)
- More maintenance overhead
End-to-End Tests
Section titled โEnd-to-End TestsโDrive the application like a real user โ browser automation (Playwright, Cypress) or API calls that hit the live system.
- Most confidence โ tests what users actually experience
- Slowest to run, most brittle
- Expensive to write and maintain
Key Principles
Section titled โKey PrinciplesโAAA (Arrange, Act, Assert):
Arrange โ set up the system under test and its dependenciesAct โ call the thing being testedAssert โ verify the expected outcomeFIRST:
- Fast โ unit tests should run in milliseconds
- Independent โ tests should not depend on each other or run order
- Repeatable โ same result every time, regardless of environment
- Self-validating โ pass or fail without manual inspection
- Timely โ written alongside (or before) the code
Test behaviour, not implementation: Test what a function does, not how it does it internally. Tests that check internal state break when you refactor.
What Makes a Good Test
Section titled โWhat Makes a Good Testโ# Bad test โ checks internal state, too tightly coupleddef test_add_to_cart(): cart = ShoppingCart() cart.add("item-1", qty=2) assert cart._items["item-1"] == 2 # internal structure
# Good test โ checks observable behaviourdef test_add_to_cart(): cart = ShoppingCart() cart.add("item-1", qty=2) assert cart.total_items() == 2 assert cart.contains("item-1")Test Coverage
Section titled โTest CoverageโCoverage measures what % of your code is executed by tests. Itโs a useful signal but not a goal in itself:
- 100% coverage doesnโt mean no bugs
- Low coverage is a red flag
- Aim for high coverage on business logic, less on boilerplate
# JavaScript (Jest)npx jest --coverage
# Python (pytest)pytest --cov=src --cov-report=html
# C# (.NET)dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcovTooling Reference
Section titled โTooling Referenceโ| Language | Unit Testing | Mocking | E2E |
|---|---|---|---|
| JavaScript/TypeScript | Jest, Vitest | Jest mocks, MSW | Playwright, Cypress |
| Python | pytest | pytest-mock, unittest.mock | Playwright |
| C# | xUnit, NUnit, MSTest | Moq, NSubstitute | Playwright |
| Go | testing (stdlib) | testify | Playwright |
| Java | JUnit | Mockito | Playwright, Selenium |