new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Vitest · Guide · all subjects

getting-started

62 notes in this subject, read out of this brain and free to use. This is page 1 of 2.

Vitest is a next generation testing framework powered by Vite

Vitest is a next generation testing framework powered by Vite. It is pronounced as 'veetest'.

Install Vitest with npm, yarn, pnpm, bun, or deno

Vitest can be installed using npm install -D vitest, yarn add -D vitest, pnpm add -D vitest, bun add -D vitest, or deno add -D vitest.

Run Vitest with npx vitest without local installation

You can run Vitest directly using npx vitest without installing it locally. The npx tool will check if the command exists in the local project's binaries, then check the system's $PATH, and install it temporarily if needed.

Add .vitest directory to .gitignore

Vitest and third-party integrations can use the .vitest directory to store generated artifacts. It is recommended to add .vitest/ to your .gitignore file.

Test files must contain .test. or .spec. in their filename

By default, tests must contain either .test. or .spec. in their file name to be recognized by Vitest.

Add test script to package.json

Add a scripts section to package.json with "test": "vitest" to enable running tests with npm run test, yarn test, or pnpm test.

Vitest reads vite.config.* by default

Vitest reads your vite.config.* by default, so existing Vite plugins and configuration work out-of-the-box. You can also create a dedicated vitest.config.* for test-specific settings.

Bun users must use bun run test instead of bun test

If you are using Bun as your package manager, make sure to use bun run test command instead of bun test, otherwise Bun will run its own test runner.

Try Vitest online on StackBlitz

You can try Vitest online on StackBlitz at vitest.new. It runs Vitest directly in the browser and is almost identical to the local setup but does not require installing anything on your machine.

Simple test example with expect and test

Here is a simple test example that verifies the output of a sum function: ```js import { expect, test } from 'vitest' import { sum } from './sum.js' test('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toBe(3) }) ```

default test timeout is 5 seconds

By default, each test has a 5-second timeout. If a test takes longer than that, it fails with a timeout error, preventing the test suite from getting stuck indefinitely.

testTimeout config option to change default timeout

The testTimeout option in your Vitest config changes the default timeout for all tests in the project. Example: test: { testTimeout: 10_000 } sets all tests to a 10-second timeout.

unhandled promise rejections cause test failure

By default, Vitest reports unhandled promise rejections as errors in the test run. If a promise rejects and nothing catches it, the test run fails even if all assertions passed. This is intentional to catch real bugs like forgotten awaits or fire-and-forget promises.

handling unhandled rejections with onUnhandledError and dangerouslyIgnoreUnhandledErrors

If your code intentionally produces unhandled rejections, you can filter specific errors with onUnhandledError config option or disable the check entirely with dangerouslyIgnoreUnhandledErrors config option.

async test function automatically waits for promise

When you make a test function async, Vitest automatically waits for the returned promise to resolve before considering the test complete. If the promise rejects, the test fails with the rejection reason.

async/await pattern for testing asynchronous code

The most straightforward approach to test asynchronous code is to make your test function async and use await to unwrap promises. This pattern reads like synchronous code and errors propagate naturally through await.

wrapping callback-based APIs in Promise

For older APIs that use callbacks instead of promises, wrap the callback in a Promise. Pass resolve as the success callback, and the test will wait until the callback is invoked.

custom timeout as third argument to test

You can set a custom timeout as the third argument to test() for tests that legitimately need more time. Example: test('name', async () => {}, 10_000) sets a 10-second timeout.

Arrange, Act, Assert test structure

Most tests follow a three-part structure: first set up the data the test needs (Arrange), then call the function or perform the action being tested (Act), then check that the result matches expectations (Check/Assert). This structure keeps tests focused and readable.

Descriptive test names

Write test names that describe the behavior being tested, not the implementation. For example, 'returns formatted price for USD' is better than 'calls Intl.NumberFormat with correct options'. When a test fails, its name should tell you what broke without needing to read the test body.

Testing edge cases

After covering main behavior, test the boundaries and edge cases: what happens at the edges, what inputs are unusual but valid, what should happen when things go wrong. Edge cases are where bugs often hide. Focus on boundaries (like 0 and 150 for age), error paths, and types of inputs the function might realistically receive.

Property-based testing with fast-check

Property-based testing is a technique where you describe properties that should hold for any input, and the testing framework generates hundreds of random inputs to try to find one that breaks. fast-check is a popular property-based testing library that integrates well with Vitest. For example, you might say 'for any valid age string, parseAge should return a non-negative integer' and let the tool find counterexamples.

Fixing bugs with tests first

When finding a bug, write a failing test first that reproduces the bug, then fix the code and watch the test turn green. This proves the bug is real, documents what was broken, and prevents regression by catching if the same bug is accidentally reintroduced. If using AI agents to fix bugs, configure them to follow this principle: reproduce the issue with a failing test first, then fix the code.

File layout: one test file per source file

The simplest organizing pattern is one test file per source file. For every utils.js, there is a utils.test.js right next to it. This makes it easy to find tests for any piece of code, and most editors show them side by side in the file tree. Some teams prefer a separate __tests__ or test directory instead. Either approach works; the important thing is consistency across the project. Vitest's include pattern matches both layouts by default.

Grouping tests with describe

When a module exports multiple functions, use describe blocks to group tests for each function. This keeps test output organized and makes it clear which function a failing test belongs to. Avoid nesting describe blocks more than one or two levels deep, as deeply nested test trees are hard to read and usually mean the source module is doing too many things.

Splitting large test files

As a project grows, some test files will get long. If a test file grows beyond a few hundred lines, consider splitting it by theme or feature area. For example, userService.test.js might become userService.creation.test.js and userService.auth.test.js. This also makes it faster to run a subset of tests during development.

Test naming matters

When a test fails in CI, its name is often the first thing someone reads. Names like 'works correctly' or 'handles edge case' do not tell you what broke. Prefer names that describe specific behavior, like 'returns 0 for an empty cart', 'throws if the email format is invalid', or 'preserves existing items when adding a new one'. Test output should read like a specification of what the module does.

Test independence with fresh setup

Create a fresh instance or reset state in every test to keep tests independent, so they can run in any order without affecting each other. If repeating the same setup in every test, use beforeEach or test.extend fixtures.

Shared module-level state in tests

When a module has shared state like a counter that is incremented across multiple calls or tests, do not make tests depend on its specific value. Instead, test relative properties like uniqueness rather than exact ID values. This prevents test failures due to execution order dependencies.

Example: formatPrice test

import { expect, test } from 'vitest' import { formatPrice } from './formatPrice.js' test('formats USD prices', () => { expect(formatPrice(10, 'USD')).toBe('$10.00') }) test('formats EUR prices', () => { expect(formatPrice(10, 'EUR')).toBe('€10.00') }) test('handles zero', () => { expect(formatPrice(0, 'USD')).toBe('$0.00') }) test('handles negative amounts', () => { expect(formatPrice(-5.5, 'USD')).toBe('-$5.50') }) test('rounds to two decimal places', () => { expect(formatPrice(10.999, 'USD')).toBe('$11.00') })

Example: parseAge test with edge cases

import { expect, test } from 'vitest' import { parseAge } from './parseAge.js' test('parses a valid age', () => { expect(parseAge('25')).toBe(25) }) test('rounds down decimal ages', () => { expect(parseAge('25.9')).toBe(25) }) test('handles zero', () => { expect(parseAge('0')).toBe(0) }) test('handles the upper boundary', () => { expect(parseAge('150')).toBe(150) }) test('throws for negative numbers', () => { expect(() => parseAge('-1')).toThrow('Invalid age: -1') }) test('throws for numbers above 150', () => { expect(() => parseAge('151')).toThrow('Invalid age: 151') }) test('throws for non-numeric strings', () => { expect(() => parseAge('abc')).toThrow('Invalid age: abc') }) test('throws for empty string', () => { expect(() => parseAge('')).toThrow('Invalid age: ') })

Example: TodoList test with describe blocks

import { describe, expect, test } from 'vitest' import { createTodoList } from './todoList.js' describe('add', () => { test('adds a new todo', () => { const list = createTodoList() const todo = list.add('Buy groceries') expect(todo.text).toBe('Buy groceries') expect(todo.completed).toBe(false) expect(list.getAll()).toHaveLength(1) }) test('assigns unique IDs to each todo', () => { const list = createTodoList() const first = list.add('First') const second = list.add('Second') expect(first.id).not.toBe(second.id) }) test('throws when text is empty', () => { const list = createTodoList() expect(() => list.add('')).toThrow('Todo text cannot be empty') }) test('throws when text is only whitespace', () => { const list = createTodoList() expect(() => list.add(' ')).toThrow('Todo text cannot be empty') }) }) describe('remove', () => { test('removes a todo by ID', () => { const list = createTodoList() const todo = list.add('Buy groceries') list.remove(todo.id) expect(list.getAll()).toHaveLength(0) }) test('keeps other items when removing one', () => { const list = createTodoList() const first = list.add('First') list.add('Second') list.remove(first.id) expect(list.getAll()).toHaveLength(1) expect(list.getAll()[0].text).toBe('Second') }) test('throws when ID does not exist', () => { const list = createTodoList() expect(() => list.remove(999)).toThrow('Todo with id 999 not found') }) }) describe('toggle', () => { test('marks a todo as completed', () => { const list = createTodoList() const todo = list.add('Buy groceries') list.toggle(todo.id) expect(list.getAll()[0].completed).toBe(true) }) test('toggles back to incomplete', () => { const list = createTodoList() const todo = list.add('Buy groceries') list.toggle(todo.id) list.toggle(todo.id) expect(list.getAll()[0].completed).toBe(false) }) test('throws when ID does not exist', () => { const list = createTodoList() expect(() => list.toggle(999)).toThrow('Todo with id 999 not found') }) }) describe('getCompleted', () => { test('returns only completed todos', () => { const list = createTodoList() const buy = list.add('Buy groceries') list.add('Clean house') list.toggle(buy.id) const completed = list.getCompleted() expect(completed).toHaveLength(1) expect(completed[0].text).toBe('Buy groceries') }) test('returns empty array when nothing is completed', () => { const list = createTodoList() list.add('Buy groceries') expect(list.getCompleted()).toHaveLength(0) }) })

Test contract definition

A test contract defines what a function promises to do for the code that calls it. The contract is defined by its inputs (arguments, configuration) and its outputs (return values, side effects, errors). Tests should verify these inputs and outputs.

Avoid testing implementation details

Tests should check behavior and output, not internal implementation details. If someone refactors the internals but the output stays the same, the test should not break. Testing internal details like which options were passed to intermediate functions or the value of intermediate variables is a pitfall.

One behavior per test

Each test should verify one specific behavior. If you find yourself writing 'and' in a test name (like 'formats price and handles errors and logs the result'), split it into separate tests.

Example: vitest config with globals enabled

import { defineConfig } from 'vitest/config' export default defineConfig({ test: { globals: true, }, })

expect function makes assertions in tests

The expect function from vitest is used to make assertions in tests. Multiple assertions can be used within a single test.

it is an alias for test function

The it function is an alias for test and behaves identically. Some people prefer it because it reads more naturally with descriptive test names. Both test and it can be mixed freely in a project.

describe creates a test suite with grouped tests

The describe function creates a test suite, which is a named group of tests. describe blocks can be nested for further organization, but shallow nesting is recommended as deeply nested tests are harder to read.

Test file naming patterns

Vitest looks for test files containing .test. or .spec. in their name across all subdirectories. The exact patterns are: **/*.test.{ts,js,mjs,cjs,tsx,jsx} and **/*.spec.{ts,js,mjs,cjs,tsx,jsx}

Customize test file patterns with include and exclude config

If the default test file patterns do not work for a project, you can customize which files are included using the include and exclude config options.

TypeScript support in Vitest

Vitest runs on top of Vite so TypeScript works out of the box. There is no extra compiler to install, no ts-jest to configure, and no separate build step needed. Name test files with .test.ts extension instead of .test.js and start writing TypeScript tests immediately.

Vitest transforms TypeScript but does not type-check during test runs

Vitest transforms TypeScript for execution but does not type-check tests during the test run. This is for speed: you get fast feedback in the terminal, and run tsc or vitest typecheck separately when you want full type checking.

test.only runs only one test and skips everything else

The .only modifier tells Vitest to run only this test or suite and skip everything else in the file. This is useful when working on a specific test and not wanting to wait for the entire suite to finish.

test.skip skips a test without removing it

The .skip modifier skips a test without removing it. This is handy when a test is temporarily broken or you want to ignore it while working on something else.

test.todo marks placeholder tests

The .todo modifier lets you mark a placeholder for a test you haven't written yet. Vitest will list it in the output so you won't forget about it.

test.for runs parameterized tests with data-driven cases

test.for lets you define test cases as data and run the same test logic for all of them. This avoids repetition when you have several test cases that only differ in inputs and expected outputs.

Placeholder types in parameterized test names

In test.for parameterized tests, %i represents integer values, %s represents strings, and %f represents floating-point numbers. These placeholders are replaced with values from each data row in the generated test names.

Use $property syntax for object fields in parameterized test names

When using test.for with objects containing multiple values, use $property in the test name to interpolate fields from the object. This is more readable than using multiple placeholders.

describe.for creates parameterized test suites

describe.for works the same way as test.for but creates a suite for each set of parameters, which is useful when multiple tests share the same parameterized setup.

test.each is an alternative to test.for with Jest compatibility

Vitest provides test.each which is similar to test.for but spreads array arguments instead of passing them as a single value and does not provide access to the Test Context. It exists mainly for Jest compatibility. Prefer test.for in new code.

globals config option enables global test imports

By default you import test, expect, describe, and other functions from vitest at the top of every test file. You can enable the globals option in your vitest config to use them as globals without importing, similar to how Jest works.

Add vitest/globals to TypeScript compilerOptions for type support

If you use TypeScript and enable the globals option, add "types": ["vitest/globals"] to your tsconfig.json compilerOptions for proper type support.

Vitest runs test files in parallel by default

Vitest runs all test files in parallel by default using child processes. Each test file runs in its own isolated context so test files do not share state with each other. This prevents tests in different files from accidentally interfering.

Tests within a single file run sequentially by default

Tests within a single file run sequentially by default, which is usually what you want since tests in the same file often share setup code. If tests are truly independent, you can opt into running them concurrently with test.concurrent to speed things up.

Example: basic test with expect

import { expect, test } from 'vitest' test('Math.sqrt works for perfect squares', () => { expect(Math.sqrt(4)).toBe(2) expect(Math.sqrt(144)).toBe(12) expect(Math.sqrt(0)).toBe(0) })

Example: test using it alias

import { expect, it } from 'vitest' it('should compute square roots', () => { expect(Math.sqrt(4)).toBe(2) })

Example: grouping tests with describe

import { describe, expect, test } from 'vitest' describe('Math.sqrt', () => { test('returns the square root of perfect squares', () => { expect(Math.sqrt(4)).toBe(2) expect(Math.sqrt(9)).toBe(3) }) test('returns NaN for negative numbers', () => { expect(Math.sqrt(-1)).toBeNaN() }) test('returns 0 for 0', () => { expect(Math.sqrt(0)).toBe(0) }) })

Example: TypeScript test

import { expect, test } from 'vitest' interface User { name: string age: number } function createUser(name: string, age: number): User { return { name, age } } test('creates a user with the correct fields', () => { const user = createUser('Alice', 30) expect(user).toEqual({ name: 'Alice', age: 30 }) expect(user.name).toBe('Alice') })

Example: parameterized test with test.for

import { expect, test } from 'vitest' test.for([ [1, 1, 2], [1, 2, 3], [2, 1, 3], ])('add(%i, %i) -> %i', ([a, b, expected]) => { expect(a + b).toBe(expected) })

Give your agent this brain