Unit Testing Quick Reference
Everything you need day‑to‑day – testing frameworks, assertions, and best practices.
What is Unit Testing?
- Tests individual units of code (functions, methods, classes)
- Automated, fast, repeatable
- Validates behaviour, catches bugs early
- Part of CI/CD pipelines
Testing Pyramid
- End‑to‑End (E2E) – small number, slow
- Integration – medium number, moderate speed
- Unit – large number, fast
Example
- Unit: Function logic, edge cases
- Integration: Database queries, API calls
- E2E: User flows (login, checkout)
Test Structure (AAA)
- Arrange – set up test data and environment
- Act – execute the code being tested
- Assert – verify the expected outcome
// Example (JavaScript/Jest) test('adds 1 + 2 to equal 3', () => { // Arrange const a = 1; const b = 2; // Act const result = add(a, b); // Assert expect(result).toBe(3); });
Testing Frameworks
JavaScript
- Jest – React, general
- Mocha – Flexible, with Chai
- Jasmine – BDD
- Vitest – Vite‑native
- Cypress – E2E (also unit)
- Playwright – E2E, modern
Python
- unittest – Built‑in
- pytest – Most popular
- nose2 – Extension of unittest
- doctest – Tests in docstrings
Java
- JUnit 5 – Most common
- TestNG – Alternative
- Mockito – Mocking
- AssertJ – Fluent assertions
Others
- RSpec – Ruby
- PHPUnit – PHP
- Go testing – Go
- Rust – Built‑in
- C# – xUnit, NUnit, MSTest
Assertions
Jest
expect(value).toBe(expected) // strict equality expect(value).toEqual(expected) // deep equality expect(value).toBeTruthy() expect(value).toBeFalsy() expect(value).toBeNull() expect(value).toBeUndefined() expect(value).toBeDefined() expect(value).toContain(item) expect(value).toHaveLength(n) expect(value).toMatch(/regex/) expect(value).toThrow() expect(value).toBeGreaterThan(n) expect(value).toBeLessThan(n) expect(value).toBeInstanceOf(Class)
pytest
assert value == expected assert value is True assert value is False assert value is None assert value in collection assert value is not None assert value != expected assert isinstance(value, Class) with pytest.raises(Exception): ...
JUnit 5
assertEquals(expected, actual)
assertNotEquals(expected, actual)
assertTrue(condition)
assertFalse(condition)
assertNull(object)
assertNotNull(object)
assertSame(expected, actual)
assertNotSame(expected, actual)
assertArrayEquals(expected, actual)
assertIterableEquals(expected, actual)
assertThrows(Exception.class, () -> { ... })
Mocking
Mockito (Java)
// Create mock MyService mockService = Mockito.mock(MyService.class); // Stub behaviour when(mockService.getValue()).thenReturn(42); // Verify interaction verify(mockService).getValue(); // Verify times called verify(mockService, times(2)).getValue(); // Spy (partial mock) MyService spy = Mockito.spy(new MyService()); // Mock with arguments when(mockService.process(anyInt())).thenReturn(100); when(mockService.process(eq(5))).thenReturn(50);
Jest (JavaScript)
// Create mock function const mockFn = jest.fn(); mockFn.mockReturnValue(42); mockFn.mockResolvedValue(42); // async // Mock module jest.mock('./api', () => ({ fetchData: jest.fn().mockResolvedValue({ data: 'mocked' }) })); // Verify calls expect(mockFn).toHaveBeenCalled(); expect(mockFn).toHaveBeenCalledTimes(2); expect(mockFn).toHaveBeenCalledWith('arg'); // Spy on object method const spy = jest.spyOn(obj, 'method');
pytest (Python)
# Using pytest‑mock from unittest.mock import Mock, patch # Create mock mock_obj = Mock() mock_obj.method.return_value = 42 # Patch with patch('module.external_call') as mock_call: mock_call.return_value = 100 result = function_under_test() # Verify mock_obj.method.assert_called_once() mock_obj.method.assert_called_with('arg') mock_obj.method.assert_called_times(2)
Test Coverage
- Line Coverage – percentage of lines executed
- Branch Coverage – percentage of branches executed
- Function Coverage – percentage of functions called
- Statement Coverage – percentage of statements executed
Coverage Tools
- JavaScript: Jest (`--coverage`), Istanbul, nyc
- Python: pytest‑cov, coverage.py
- Java: JaCoCo, Cobertura
- Go: `go test -cover`
- Rust: `cargo tarpaulin`
Target Coverage
- 80%+ – Good target for most projects
- 100% – Desirable but not always practical
- Focus on critical paths and edge cases
Test Doubles
Mock
- Stubs behaviour and verifies interactions
- Expectations are set before the test
- Use when testing interactions
Stub
- Provides canned responses
- No verification of interactions
- Use when testing state
Spy
- Wraps real object and records calls
- Verifies interactions after the fact
- Use for partial mocks
Fake
- Simplified working implementation
- In‑memory database, in‑memory cache
- Use for integration tests
Test Annotations (JUnit 5)
| Annotation | Description |
|---|---|
@Test |
Marks a test method |
@BeforeEach |
Runs before each test |
@AfterEach |
Runs after each test |
@BeforeAll |
Runs once before all tests |
@AfterAll |
Runs once after all tests |
@Disabled |
Disables a test |
@DisplayName |
Sets a display name |
@Tag |
Labels a test |
@Timeout |
Adds a timeout |
@Nested |
Nested test class |
@ParameterizedTest |
Parameterised test |
Parameterised Tests
JUnit 5
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void testIsOdd(int number) {
assertTrue(isOdd(number));
}
@ParameterizedTest
@CsvSource({ "1,2,3", "4,5,9", "10,20,30" })
void testSum(int a, int b, int expected) {
assertEquals(expected, sum(a, b));
}
pytest
# @pytest.mark.parametrize
@pytest.mark.parametrize("a, b, expected", [
(1, 2, 3),
(4, 5, 9),
(10, 20, 30)
])
def test_add(a, b, expected):
assert add(a, b) == expected
Jest
test.each([
[1, 2, 3],
[4, 5, 9],
[10, 20, 30]
])('adds %i + %i to equal %i', (a, b, expected) => {
expect(add(a, b)).toBe(expected);
});
Test-Driven Development (TDD)
- Red – Write a failing test
- Green – Write the minimum code to pass the test
- Refactor – Improve the code while keeping tests green
Behaviour-Driven Development (BDD)
- Uses Given‑When‑Then format
- Focuses on behaviour, not implementation
- Tools: Cucumber, Jasmine, Mocha
Feature: Calculator
Scenario: Add two numbers
Given I have numbers 1 and 2
When I add them
Then the result should be 3
Best Practices
- Test one thing per test – single assertion (or small group)
- Use descriptive names – `test_add_when_both_positive_returns_sum`
- Keep tests fast – avoid network, DB, file I/O
- Use mocks for dependencies – isolate the unit
- Test edge cases – null, empty, negative, max, min
- Test happy path first – then error cases
- Run tests in CI – every commit
- Maintain test code – don't let it become legacy
- Use coverage tools – target 80%+
- Write tests before fixing bugs – prevents regression
- Treat test code like production code – clean, maintainable
- Use test factories – reduce duplication
- Use assertions with messages – helpful for debugging
- Don't mock what you don't own – use integration tests for external services
📌 Quick Reference
Frameworks: JUnit (Java), pytest (Python), Jest (JS), RSpec (Ruby)
AAA: Arrange, Act, Assert
Assertions: assertEquals, assertTrue, assertThrows, toBe, toEqual
Mocking: Mockito (Java), jest.fn() (JS), unittest.mock (Python)
Coverage: JaCoCo, pytest‑cov, Jest --coverage, Istanbul
TDD: Red → Green → Refactor
BDD: Given‑When‑Then, Cucumber, Gherkin
Best practices: one assertion per test, fast tests, edge cases, clean test code
AAA: Arrange, Act, Assert
Assertions: assertEquals, assertTrue, assertThrows, toBe, toEqual
Mocking: Mockito (Java), jest.fn() (JS), unittest.mock (Python)
Coverage: JaCoCo, pytest‑cov, Jest --coverage, Istanbul
TDD: Red → Green → Refactor
BDD: Given‑When‑Then, Cucumber, Gherkin
Best practices: one assertion per test, fast tests, edge cases, clean test code