Unit Testing Guide

Good Tests: The FIRST Principles

PrincipleMeaning
FastRun in milliseconds; no sleep, no I/O
IndependentNo shared state between tests; order doesn't matter
RepeatableSame result every time; no randomness, no time dependencies
Self-validatingPass or fail automatically โ€” no manual inspection needed
TimelyWritten at the same time as production code (TDD) or immediately after

Test Structure: AAA Pattern

// JavaScript (Vitest / Jest) โ€” AAA pattern import { describe, it, expect, beforeEach } from 'vitest'; import { ShoppingCart } from './cart'; describe('ShoppingCart', () => { let cart; beforeEach(() => { cart = new ShoppingCart(); // fresh state per test }); it('calculates total with no items', () => { // Arrange โ€” (done in beforeEach) // Act const total = cart.total(); // Assert expect(total).toBe(0); }); it('calculates total with multiple items including discount', () => { // Arrange cart.add({ id: 1, price: 100, qty: 2 }); cart.add({ id: 2, price: 50, qty: 1 }); cart.applyDiscount(10); // 10% discount // Act const total = cart.total(); // Assert expect(total).toBe(225); // (200 + 50) * 0.9 }); it('throws when adding item with negative price', () => { expect(() => cart.add({ id: 1, price: -5, qty: 1 })) .toThrow('Price must be non-negative'); }); });

Go Unit Tests

// Go โ€” table-driven tests (idiomatic) package calculator_test import ( "testing" "github.com/myapp/calculator" ) func TestAdd(t *testing.T) { tests := []struct { name string a, b int want int }{ {"positive numbers", 2, 3, 5}, {"negative + positive", -2, 5, 3}, {"both negative", -3, -4, -7}, {"zero values", 0, 0, 0}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got := calculator.Add(tc.a, tc.b) if got != tc.want { t.Errorf("Add(%d, %d) = %d, want %d", tc.a, tc.b, got, tc.want) } }) } } // Using testify for cleaner assertions func TestDivide(t *testing.T) { result, err := calculator.Divide(10, 2) require.NoError(t, err) assert.Equal(t, 5.0, result) _, err = calculator.Divide(10, 0) assert.ErrorIs(t, err, calculator.ErrDivisionByZero) }