Anatomy of a unit test

A unit test is a piece of code that checks if a small part of your application works correctly.

Let’s start with the simplest form of a test:

import { expect, test } from "vitest";
import { functionToTest } from "./yourModule";

test("description of what the test is checking", () => {
  // Arrange: Set up the test data
  const input = "some input";
  const expectedOutput = "expected output";

  // Act: Call the function being tested
  const result = functionToTest(input);

  // Assert: Check if the result matches the expected output
  expect(result).toBe(expectedOutput);
});

Let’s break this down:

  1. Import statements: We bring in the tools we need from Vitest and the function we want to test.
  2. Test function: We use test() to create a new test. We could also use it() instead of test() as they aliases. We give it a label that describes what we’re testing.
  3. Arrange: We set up the data we’ll use in our test.
  4. Act: We run the function we’re testing.
  5. Assert: We check if the result is what we expected.

As you write more tests, you’ll want to keep them organized.

This is where describe comes in. It helps you group related tests together:

import { expect, describe, it } from "vitest";
import { functionToTest } from "./yourModule";

describe("functionToTest", () => {
  it("description of what the test is checking", () => {
    // Arrange: Set up the test data
    const input = "some input";
    const expectedOutput = "expected output";

    // Act: Call the function being tested
    const result = functionToTest(input);

    // Assert: Check if the result matches the expected output
    expect(result).toBe(expectedOutput);
  });

  it("a different description of what the test is checking", () => {
    // Arrange: Set up the test data
    const input = "some input";
    const expectedOutput = "expected output";

    // Act: Call the function being tested
    const result = functionToTest(input);

    // Assert: Check if the result matches the expected output
    expect(result).toBe(expectedOutput);
  });
});

Think of describe like creating a folder to keep similar tests together. In this example:

  • All tests are related to the functionToTest function
  • Each test checks a different scenario
  • The structure makes it easy to understand what we’re testing

Understanding Matchers

When we write tests, we need to check if our results are what we expect.

Vitest provides special functions called “matchers” to do these checks. Here are the most common ones:

Basic Matchers

// toBe: Use for simple values (numbers, strings, booleans)
expect(2 + 2).toBe(4);
expect("hello").toBe("hello");
expect(true).toBe(true);

// toEqual: Use for objects and arrays
const user = { name: "John", age: 30 };
expect(user).toEqual({ name: "John", age: 30 });

const numbers = [1, 2, 3];
expect(numbers).toEqual([1, 2, 3]);

Truthiness Matchers

// toBeNull: Checks if something is null
expect(null).toBeNull();

// toBeDefined: Checks if something has been defined
let name = "John";
expect(name).toBeDefined();

// toBeUndefined: Checks if something is undefined
let age;
expect(age).toBeUndefined();

// toBeTruthy: Checks if something is true-like
expect("hello").toBeTruthy();
expect(1).toBeTruthy();

// toBeFalsy: Checks if something is false-like
expect("").toBeFalsy();
expect(0).toBeFalsy();

Number Matchers

// toBeGreaterThan: Checks if a number is bigger
expect(10).toBeGreaterThan(5);

// toBeLessThan: Checks if a number is smaller
expect(5).toBeLessThan(10);

// toBeGreaterThanOrEqual: Checks if a number is bigger or equal
expect(10).toBeGreaterThanOrEqual(10);

// toBeLessThanOrEqual: Checks if a number is smaller or equal
expect(5).toBeLessThanOrEqual(5);

String Matchers

// toMatch: Checks if a string matches a pattern (regex)
expect("hello@example.com").toMatch(/@/);
expect("hello world").toMatch(/world/);

// toContain: Checks if a string contains another string
expect("hello world").toContain("hello");

Array Matchers

const shoppingList = ["milk", "bread", "eggs"];

// toContain: Checks if an array contains an item
expect(shoppingList).toContain("milk");

// toHaveLength: Checks the length of an array
expect(shoppingList).toHaveLength(3);

Object Matchers

const user = {
  name: "John",
  age: 30,
  email: "john@example.com",
};

// toHaveProperty: Checks if an object has a property
expect(user).toHaveProperty("name");

// toHaveProperty with value: Checks property and its value
expect(user).toHaveProperty("name", "John");

Not Matcher

You can use .not before any matcher to check for the opposite:

// Checking that something is NOT equal
expect(2 + 2).not.toBe(5);

// Checking that an array does NOT contain something
const numbers = [1, 2, 3];
expect(numbers).not.toContain(4);

What We Learned

In this lesson, we:

  • Learned how to write basic unit tests
  • Understood the Arrange-Act-Assert pattern
  • Learned how to group tests with describe
  • Discovered different types of matchers and when to use them

Remember:

  • Always give your tests clear descriptions
  • Use the right matcher for your test case

    • toBe() for simple values (numbers, strings, booleans)
    • toEqual() for objects and arrays
  • Group related tests together using describe
  • Follow the Arrange-Act-Assert pattern

Video

Looking at the structure of a unit test, how to group tests and what matchers are.

Video

Tags: