## What is Unit Testing?
Unit testing verifies that individual pieces of code (functions, methods, components) work correctly in isolation. Each test focuses on one small unit, checking if it produces expected outputs for given inputs.
Think of it as quality control in manufacturing. Before assembling a car, you test each part - brakes, engine, lights - individually. If every part works alone, the assembled car is more likely to work.
## Why Unit Testing Matters
**Catch Bugs Early**: Find problems immediately after writing code, not weeks later in production.
**Refactoring Confidence**: Change code fearlessly knowing tests will catch if you break something.
**Documentation**: Tests show how code is supposed to work. New developers read tests to understand functionality.
**Faster Development**: Seems slower initially but saves massive time debugging production issues.
## How Unit Tests Work
Write a test that calls your function with specific inputs and checks if the output matches expectations.
**Example (JavaScript)**:
```javascript
// Function to test
function add(a, b) {
return a + b;
}
// Unit test
test("add function works correctly", () => {
expect(add(2, 3)).toBe(5);
expect(add(-1, 1)).toBe(0);
expect(add(0, 0)).toBe(0);
});
```
If the function returns unexpected results, the test fails immediately. You know exactly what broke.
## What to Test
**Happy Path**: Normal inputs producing expected outputs.
**Edge Cases**: Empty strings, zero values, negative numbers, null values.
**Error Conditions**: Invalid inputs should throw proper errors.
**Boundary Values**: Test limits - maximum values, minimum values.
Do not test framework code or third-party libraries. They have their own tests. Test your logic.
## Common Testing Frameworks
**JavaScript**: Jest, Mocha, Vitest
**Python**: pytest, unittest
**Java**: JUnit, TestNG
**Ruby**: RSpec, Minitest
All follow similar patterns - arrange inputs, act (call function), assert results.
## Unit Test Structure (AAA Pattern)
**Arrange**: Set up test data and conditions.
**Act**: Execute the function being tested.
**Assert**: Verify the result matches expectations.
```javascript
test("user registration", () => {
// Arrange
const userData = { email: "test@example.com", password: "secret123" };
// Act
const result = registerUser(userData);
// Assert
expect(result.success).toBe(true);
expect(result.userId).toBeDefined();
});
```
## Test Coverage
Test coverage measures what percentage of your code is executed by tests.
**100% coverage** does not mean bug-free code. You can test every line but miss critical edge cases.
**70-80% coverage** is realistic for most projects. Focus on critical business logic and complex functions.
**Do not chase coverage numbers**. Write meaningful tests, not tests just to increase percentages.
## Mocking and Stubbing
Tests should be isolated. If your function calls an API or database, mock those dependencies.
**Mock**: Replace real API/database with fake version that returns predictable data.
**Why**: Tests should not depend on external services. They should run fast and reliably anywhere.
```javascript
// Mock API call
jest.mock("./api");
api.fetchUser.mockResolvedValue({ name: "John", email: "john@example.com" });
test("displays user info", async () => {
const user = await getUserInfo(123);
expect(user.name).toBe("John");
});
```
## Test-Driven Development (TDD)
Some developers write tests before writing code:
1. Write a failing test for desired functionality
2. Write minimal code to make test pass
3. Refactor code while keeping tests green
4. Repeat
This ensures every line of code has a corresponding test and meets requirements.
## Common Mistakes
**Testing implementation details**: Test what code does, not how it does it. Internal variable names can change without breaking functionality.
**Tests too large**: Unit tests should be small and fast. If a test does too much, split it.
**No assertions**: Tests must verify results. Simply running code without checking outputs is useless.
**Ignoring failing tests**: If tests fail, fix them immediately. Ignored tests become meaningless noise.
## Benefits in Real Projects
**Stripe**: Heavy test coverage prevents payment processing bugs that could cost millions.
**Facebook**: Thousands of tests run on every code commit. Bugs caught before reaching production.
**Startups**: Faster development cycles because developers trust their changes will not break existing features.
## The ROI of Testing
**Short term**: Writing tests feels slower. You write more code and think through edge cases.
**Long term**: Massive time savings. Fewer production bugs, faster debugging, confident refactoring, easier onboarding.
Companies with good test coverage ship features faster and with fewer bugs. The investment pays off quickly.
## Getting Started
Start small. Add tests for new code you write. Gradually add tests for critical existing code (authentication, payments, core business logic).
Use testing frameworks recommended for your language. Follow examples in documentation. Testing becomes natural with practice.
Unit testing is not optional for professional development. It is a fundamental skill that separates hobbyists from professional engineers.