Testing with Vitest in Astro
This guide will walk you through setting up and running tests with Vitest in your Astro project.
Step 1: Install Dependencies
First, install Vitest and related testing libraries:
pnpm add -D vitest @testing-library/react @testing-library/jest-dom happy-dom @types/node @vitejs/plugin-react
These packages provide:
vitest
: The testing framework@testing-library/react
: Utilities for testing React components@testing-library/jest-dom
: Custom jest matchers for DOM testinghappy-dom
: A lightweight browser environment for testing@vitejs/plugin-react
: React support for Vite/Vitest
Step 2: Create Vitest Configuration
Create a vitest.config.ts
file in your project root:
/// <reference types="vitest" />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
test: {
environment: 'happy-dom',
globals: true,
setupFiles: './src/test/setup.ts',
include: ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
exclude: ['node_modules', 'dist', '.idea', '.git', '.cache']
}
});
Step 3: Create Test Setup File
Create a test setup file at src/test/setup.ts
:
import '@testing-library/jest-dom';
import { cleanup } from '@testing-library/react';
import { afterEach } from 'vitest';
// Cleanup after each test
afterEach(() => {
cleanup();
});
Step 4: Update Package.json Scripts
Add test scripts to your package.json:
{
"scripts": {
"test": "vitest",
"test:ui": "vitest --ui",
"test:run": "vitest run",
"coverage": "vitest run --coverage"
}
}
Step 5: Writing Your First Test
Create a test file for a component. For example, src/components/ModeToggle.test.tsx
:
import { describe, expect, it } from 'vitest';
import { render, screen } from '@testing-library/react';
import { ModeToggle } from './ModeToggle';
import userEvent from '@testing-library/user-event';
describe('ModeToggle', () => {
it('renders the toggle button', () => {
render(<ModeToggle />);
const button = screen.getByRole('button');
expect(button).toBeInTheDocument();
});
it('changes theme when clicked', async () => {
render(<ModeToggle />);
const button = screen.getByRole('button');
await userEvent.click(button);
expect(document.documentElement.classList).toContain('dark');
});
});
Step 6: Running Tests
You can run tests using several commands:
- Run tests in watch mode (development):
pnpm test
- Run tests once (CI/CD):
pnpm test:run
- Run tests with UI interface:
pnpm test:ui
- Generate coverage report:
pnpm coverage
Test Structure Best Practices
-
File Organization:
- Place test files next to the components they test
- Use
.test.tsx
or.spec.tsx
suffix - Group related tests in describe blocks
-
Naming Conventions:
- Use descriptive test names
- Follow the pattern: βit should [expected behavior]β
- Group related tests in meaningful describe blocks
-
Testing Patterns:
describe('Component', () => {
// Setup/teardown hooks if needed
beforeEach(() => {
// Setup code
});
afterEach(() => {
// Cleanup code
});
// Test cases
it('should render correctly', () => {
// Test implementation
});
it('should handle user interaction', async () => {
// Test implementation
});
});
Common Testing Utilities
- Rendering Components:
import { render, screen } from '@testing-library/react';
render(<YourComponent />);
- Finding Elements:
// By role
const button = screen.getByRole('button');
// By text
const element = screen.getByText('Hello');
// By test ID
const custom = screen.getByTestId('custom-element');
- User Interactions:
import userEvent from '@testing-library/user-event';
await userEvent.click(button);
await userEvent.type(input, 'Hello');
- Assertions:
expect(element).toBeInTheDocument();
expect(element).toHaveTextContent('Hello');
expect(element).toBeVisible();
Debugging Tests
- Use the UI interface for better debugging:
pnpm test:ui
- Use console.log in tests:
it('should work', () => {
console.log('Debug info:', someValue);
expect(true).toBe(true);
});
- Use the debug utility:
import { screen } from '@testing-library/react';
screen.debug();
Remember to write tests that focus on behavior rather than implementation details, and aim for high test coverage of critical application logic.