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:
- Import statements: We bring in the tools we need from Vitest and the function we want to test.
- Test function: We use
test()
to create a new test. We could also useit()
instead oftest()
as they aliases. We give it a label that describes what we’re testing. - Arrange: We set up the data we’ll use in our test.
- Act: We run the function we’re testing.
- Assert: We check if the result is what we expected.
Grouping related tests
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