Unit / JS · Junior & Senior

Jest & Vitest

The dominant JavaScript and TypeScript unit testing frameworks — Jest for established React and Node projects, Vitest for modern Vite-based apps. Near-identical APIs, different performance characteristics.

Junior Senior
01

The Hook: Why Jest/Vitest?

Imagine building a car. You wouldn't wait until the entire vehicle is assembled to see if the headlights turn on. You'd test the bulb before it ever goes into the socket. In software, Jest and Vitest are your bulb testers.

Unit tests are the fastest, cheapest, and most reliable signal in your quality suite. While a Playwright test might take 10 seconds to log in and check a button, a Vitest test can check 100 different logic combinations in 10 milliseconds.

02

The Rule: The 1-1-1 Principle

A unit test follows the 1-1-1 rule to remain effective:

  • 1 Unit: Test exactly one function, component, or class in isolation.
  • 1 Path: Test one specific logical branch (e.g., "Success" is one test, "Error" is another).
  • 1 Reason to Fail: If the test fails, it should tell you exactly where the code is broken without any investigation.
03

The Analogy: Screws vs. Assemblies

Think of Unit Tests as checking individual screws for the right thread. Integration Tests are checking if two boards can be screwed together. End-to-End Tests are checking if the finished table can hold the weight of a feast.

If you don't test the screws (Unit), you'll spend hours wondering why the table (E2E) is wobbly, only to find a single stripped thread at the very bottom.

04

The Setup: Getting Ready

For modern projects using Vite, we use Vitest. It's built for speed and requires almost zero configuration.

npm install -D vitest

In your package.json, add this script:

"scripts": {
  "test": "vitest"
}
05

The First Script: Hello Logic

Create a file named math.test.ts. Notice the .test.ts suffix — this is how the runner finds your tests.

import { expect, test } from 'vitest'

function add(a, b) {
  return a + b
}

test('adds 1 + 2 to equal 3', () => {
  expect(add(1, 2)).toBe(3)
})

Run it with npm test. You'll see a green "PASS" in your terminal in a fraction of a second.

06

The "Gotcha": The Mocking Trap

The most common mistake junior testers make is testing external dependencies (like an API) in a unit test. If your unit test calls a real database, it's no longer a unit test — it's an integration test.

The Fix: Use Mocks. If your function calls fetch(), you "mock" that fetch to return a fake JSON response instantly. This ensures your test is only measuring your code, not the network's reliability.

07

The Framework: AAA Pattern

Professional unit tests follow the AAA Pattern:

  • Arrange: Set up the data and conditions (e.g., create a fake user).
  • Act: Call the function you are testing.
  • Assert: Check that the result matches your expectation.
test('calculates discount correctly', () => {
  // Arrange
  const price = 100
  const discount = 0.2
  
  // Act
  const result = calculateTotal(price, discount)
  
  // Assert
  expect(result).toBe(80)
})
08

The NZ Example: GST Calculator

Let's test a real New Zealand business requirement: calculating GST. In NZ, GST is 15%.

// gst.ts
export function calculateGst(amount: number): number {
  return amount * 0.15
}

// gst.test.ts
import { calculateGst } from './gst'

test('calculates 15% GST on $100', () => {
  expect(calculateGst(100)).toBe(15)
})

test('handles zero amount', () => {
  expect(calculateGst(0)).toBe(0)
})

test('rounds to 2 decimal places', () => {
  // This test will fail if your function doesn't round!
  expect(calculateGst(10.55)).toBe(1.58) 
})
09

The Pro Move: Coverage Thresholds

Don't just run tests; measure what you've missed. Use the --coverage flag to see exactly which lines of code aren't tested.

Senior Strategy: In professional NZ teams, we often set "Coverage Thresholds" in the CI pipeline. If your new code drops the total project coverage below 80%, the build fails. This forces quality to be a continuous habit, not an afterthought.

10

The Challenge: Your Turn

Write a function that validates a New Zealand Postcode. It must be exactly 4 digits and numeric.

Your Task: Write four tests: one for a valid postcode (e.g., 6011), one for 3 digits, one for 5 digits, and one containing letters. Use expect().toBe(false) for the invalid ones.

← All tools JUnit & TestNG →