Vitest

Vitest is a fast unit test framework powered by Vite. It offers speed and a familiar developer experience, especially for those using Vue, React, Svelte, or other modern JavaScript frameworks. It's designed to be compatible with Jest.

Detailed explanation

Vitest is a modern testing framework that leverages the speed and configuration of Vite, a popular build tool. This integration allows for incredibly fast test execution, especially in projects that already use Vite for development. Vitest aims to provide a Jest-compatible API, making it easy to migrate existing Jest tests or to use Vitest as a drop-in replacement.

One of the key advantages of Vitest is its speed. Because it uses Vite's transform pipeline, tests can be executed much faster than with traditional test runners like Jest. This is particularly noticeable in large projects with many test files. Vitest also supports features like hot module replacement (HMR), which allows tests to be re-run automatically when code changes are detected, providing instant feedback during development.

Installation and Setup

To get started with Vitest, you'll need Node.js and npm or yarn installed. You can install Vitest as a dev dependency using npm:

npm install -D vitest

Or using yarn:

yarn add -D vitest

Next, you'll need to configure Vitest in your package.json file by adding a test script:

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

You can then run your tests using:

npm test

Or:

yarn test

Writing Tests

Vitest uses a similar API to Jest, so you can use familiar functions like describe, it, expect, beforeEach, afterEach, beforeAll, and afterAll. Here's a simple example of a Vitest test:

// math.test.js
import { describe, it, expect } from 'vitest';
import { add } from './math';
 
describe('add', () => {
  it('should add two numbers correctly', () => {
    expect(add(1, 2)).toBe(3);
  });
 
  it('should handle negative numbers', () => {
    expect(add(-1, 2)).toBe(1);
  });
});

In this example, we're testing a simple add function. The describe block groups related tests together, and the it blocks define individual test cases. The expect function is used to make assertions about the results of the code being tested.

Configuration

Vitest can be configured using a vitest.config.js or vitest.config.ts file in your project root. This file allows you to customize various aspects of Vitest, such as the test environment, reporters, and coverage settings.

// vitest.config.js
import { defineConfig } from 'vitest/config';
 
export default defineConfig({
  test: {
    environment: 'jsdom', // or 'node'
    globals: true,
    coverage: {
      reporter: ['text', 'json', 'html'],
    },
  },
});

In this example, we're configuring Vitest to use the jsdom environment, which is useful for testing code that runs in a browser. We're also enabling globals, which allows you to use functions like describe and it without importing them explicitly. Finally, we're configuring code coverage to generate text, JSON, and HTML reports.

Mocking and Spying

Vitest provides built-in support for mocking and spying, which is essential for testing code that depends on external dependencies or has side effects. You can use the vi object (or jest if you prefer) to create mocks and spies.

import { describe, it, expect, vi } from 'vitest';
import { fetchData } from './api';
import { processData } from './dataProcessor';
 
describe('processData', () => {
  it('should process data correctly', async () => {
    const mockFetchData = vi.fn().mockResolvedValue([{ id: 1, name: 'Test' }]);
    global.fetch = mockFetchData; // Mock the global fetch function
 
    const result = await processData();
 
    expect(result).toEqual([{ id: 1, name: 'Test', processed: true }]);
    expect(mockFetchData).toHaveBeenCalledTimes(1);
  });
});

In this example, we're mocking the fetchData function to return a predefined value. This allows us to test the processData function in isolation, without relying on the actual API. We're also using vi.fn() to create a mock function and expect(mockFetchData).toHaveBeenCalledTimes(1) to verify that the mock function was called.

Code Coverage

Vitest supports code coverage reporting out of the box. You can enable code coverage by setting the coverage option in your vitest.config.js file.

// vitest.config.js
import { defineConfig } from 'vitest/config';
 
export default defineConfig({
  test: {
    coverage: {
      reporter: ['text', 'json', 'html'],
      include: ['src/**'],
      exclude: ['src/types/**'],
    },
  },
});

In this example, we're configuring code coverage to generate text, JSON, and HTML reports. We're also specifying which files to include and exclude from the coverage analysis.

Best Practices

  • Write focused tests: Each test should focus on a specific aspect of the code being tested.
  • Use descriptive test names: Test names should clearly describe what the test is verifying.
  • Keep tests independent: Tests should not depend on each other.
  • Use mocks and spies: Use mocks and spies to isolate code and verify interactions.
  • Run tests frequently: Run tests frequently to catch errors early.
  • Use code coverage: Use code coverage to identify areas of the code that are not being tested.
  • Keep tests fast: Optimize tests for speed to ensure a fast feedback loop.

Vitest is a powerful and versatile testing framework that can significantly improve the speed and efficiency of your testing workflow. Its compatibility with Jest and its integration with Vite make it a great choice for modern JavaScript projects. By following the best practices outlined above, you can write effective and maintainable tests that help ensure the quality of your code.

Further reading