pramoddutta/qaskills/pramoddutta-vitest

Trust tier 2
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Modern JavaScript and TypeScript testing with Vitest, covering unit testing, integration testing, mocking, snapshots, browser mode, and Vite integration.

Install

npx vskill@latest install pramoddutta/qaskills/pramoddutta-vitest

SKILL.md

Vitest Testing Skill

You are an expert software engineer specializing in testing with Vitest. When the user asks you to write, review, or debug Vitest tests, follow these detailed instructions.

Core Principles

  1. Blazing fast -- Vitest is designed for speed with native ESM support and smart test running.
  2. Vite-native -- Leverages Vite's config, transformers, and plugins for seamless integration.
  3. Jest-compatible API -- Familiar API makes migration from Jest straightforward.
  4. Watch mode first -- Vitest excels at watch mode with instant feedback.
  5. Test isolation -- Each test should be independent and deterministic.

Project Structure

project/
src/
components/
Button.tsx
Button.test.tsx
services/
user.service.ts
user.service.test.ts
utils/
validators.ts
validators.test.ts
tests/
integration/
api.test.ts
db.test.ts
fixtures/
test-data.ts
setup.ts
vitest.config.ts
vite.config.ts

Configuration

Basic Config
typescript
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './tests/setup.ts',
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'tests/',
'**/*.test.ts',
'**/*.spec.ts',
'**/types/',
],
thresholds: {
lines: 80,
functions: 80,
branches: 80,
statements: 80,
},
},
include: ['**/*.{test,spec}.{ts,tsx}'],
exclude: ['node_modules', 'dist', 'build'],
testTimeout: 10000,
hookTimeout: 10000,
},
});
Advanced Config with Multiple Environments
typescript
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node',
environmentMatchGlobs: [
['**/*.test.tsx', 'jsdom'],
['**/*.browser.test.ts', 'jsdom'],
['**/*.node.test.ts', 'node'],
['**/*.edge.test.ts', 'edge-runtime'],
],
poolOptions: {
threads: {
singleThread: false,
isolate: true,
},
},
coverage: {
provider: 'v8',
include: ['src/**/*.{ts,tsx}'],
},
benchmark: {
include: ['**/*.bench.{ts,tsx}'],
},
},
});

Writing Tests

Basic Test Structure
typescript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { sum, multiply } from './math';
describe('Math utilities', () => {
it('should add two numbers', () => {
expect(sum(2, 3)).toBe(5);
});
it('should multiply two numbers', () => {
expect(multiply(4, 5)).toBe(20);
});
it('should handle zero', () => {
expect(sum(0, 0)).toBe(0);
expect(multiply(5, 0)).toBe(0);
});
it('should handle negative numbers', () => {
expect(sum(-1, 1)).toBe(0);
expect(multiply(-2, 3)).toBe(-6);
});
});
Testing Classes and Services
typescript
// user.service.ts
export class UserService {
constructor(
private apiClient: ApiClient,
private cache: Cache
) {}
async getUser(id: string): Promise<User> {
const cached = await this.cache.get(`user:${id}`);
if (cached) return JSON.parse(cached);
const user = await this.apiClient.get(`/users/${id}`);
await this.cache.set(`user:${id}`, JSON.stringify(user), 3600);
return user;
}
async createUser(data: CreateUserDto): Promise<User> {
return this.apiClient.post('/users', data);
}
}
typescript
// user.service.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { UserService } from './user.service';
describe('UserService', () => {
let userService: UserService;
let mockApiClient: any;
let mockCache: any;
beforeEach(() => {
mockApiClient = {
get: vi.fn(),
post: vi.fn(),
};
mockCache = {
get: vi.fn(),
set: vi.fn(),
};
userService = new UserService(mockApiClient, mockCache);
});
describe('getUser', () => {
it('should return cached user if available', async () => {
const cachedUser = { id: '1', name: 'Cached User' };
mockCache.get.mockResolvedValue(JSON.stringify(cachedUser));
const result = await userService.getUser('1');
expect(result).toEqual(cachedUser);
expect(mockCache.get).toHaveBeenCalledWith('user:1');
expect(mockApiClient.get).not.toHaveBeenCalled();
});
it('should fetch user from API if not cached', async () => {
const user = { id: '1', name: 'API User' };
mockCache.get.mockResolvedValue(null);
mockApiClient.get.mockResolvedValue(user);
const result = await userService.getUser('1');
expect(result).toEqual(user);
expect(mockApiClient.get).toHaveBeenCalledWith('/users/1');
expect(mockCache.set).toHaveBeenCalledWith(
'user:1',
JSON.stringify(user),
3600
);
});
});
describe('createUser', () => {
it('should create a new user', async () => {
const newUser = { id: '2', name: 'New User', email: 'new@example.com' };
mockApiClient.post.mockResolvedValue(newUser);
const result = await userService.createUser({
name: 'New User',
email: 'new@example.com',
});
expect(result).toEqual(newUser);
expect(mockApiClient.post).toHaveBeenCalledWith('/users', {
name: 'New User',
email: 'new@example.com',
});
});
});
});

Mocking Patterns

Function Mocking
typescript
import { vi, describe, it, expect } from 'vitest';
describe('Function mocking', () => {
it('should mock a function', () => {
const mockFn = vi.fn();
mockFn.mockReturnValue(42);
expect(mockFn()).toBe(42);
expect(mockFn).toHaveBeenCalledOnce();
});
it('should mock implementation', () => {
const mockFn = vi.fn((x: number) => x * 2);
expect(mockFn(5)).toBe(10);
expect(mockFn).toHaveBeenCalledWith(5);
});
it('should mock resolved value', async () => {
const mockFn = vi.fn();
mockFn.mockResolvedValue({ id: 1, name: 'Test' });
const result = await mockFn();
expect(result).toEqual({ id: 1, name: 'Test' });
});
it('should mock rejected value', async () => {
const mockFn = vi.fn();
mockFn.mockRejectedValue(new Error('Failed'));
await expect(mockFn()).rejects.toThrow('Failed');
});
});
Module Mocking
typescript
// __mocks__/axios.ts
import { vi } from 'vitest';
export default {
get: vi.fn(),
post: vi.fn(),
put: vi.fn(),
delete: vi.fn(),
create: vi.fn(() => ({
get: vi.fn(),
post: vi.fn(),
})),
};
typescript
// api.test.ts
import { describe, it, expect, vi } from 'vitest';
vi.mock('axios');
import axios from 'axios';
import { fetchUser } from './api';
describe('API', () => {
it('should fetch user data', async () => {
const mockUser = { id: 1, name: 'Test User' };
vi.mocked(axios.get).mockResolvedValue({ data: mockUser });
const user = await fetchUser('1');
expect(user).toEqual(mockUser);
expect(axios.get).toHaveBeenCalledWith('/api/users/1');
});
});
Partial Module Mocking
typescript
import { vi } from 'vitest';
// Mock only specific exports
vi.mock('./utils', async () => {
const actual = await vi.importActual('./utils');
return {
...actual,
fetchData: vi.fn(),
};
});
Spying
typescript
import { vi, describe, it, expect } from 'vitest';
describe('Spying', () => {
it('should spy on console.error', () => {
const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
console.error('Test error');
expect(spy).toHaveBeenCalledWith('Test error');
spy.mockRestore();
});
it('should spy on object method', () => {
const obj = {
method: (x: number) => x * 2,
};
const spy = vi.spyOn(obj, 'method');
obj.method(5);
expect(spy).toHaveBeenCalledWith(5);
expect(spy).toHaveReturnedWith(10);
});
});

Timer Mocking

typescript
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
describe('Timer tests', () => {
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.restoreAllMocks();
});
it('should debounce function calls', () => {
const fn = vi.fn();
const debounced = debounce(fn, 300);
debounced();
debounced();
debounced();
expect(fn).not.toHaveBeenCalled();
vi.advanceTimersByTime(300);
expect(fn).toHaveBeenCalledOnce();
});
it('should throttle function calls', () => {
const fn = vi.fn();
const throttled = throttle(fn, 100);
throttled();
expect(fn).toHaveBeenCalledOnce();
throttled();
expect(fn).toHaveBeenCalledOnce(); // still once
vi.advanceTimersByTime(100);
throttled();
expect(fn).toHaveBeenCalledTimes(2);
});
it('should handle setTimeout', () => {
const callback = vi.fn();
setTimeout(callback, 1000);
expect(callback).not.toHaveBeenCalled();
vi.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalledOnce();
});
});

Snapshot Testing

typescript
import { describe, it, expect } from 'vitest';
describe('Snapshot tests', () => {
it('should match snapshot', () => {
const data = {
id: 1,
name: 'Test User',
roles: ['admin', 'user'],
createdAt: new Date('2024-01-01'),
};
expect(data).toMatchSnapshot();
});
it('should match inline snapshot', () => {
const formatted = formatUserName({ first: 'John', last: 'Doe' });
expect(formatted).toMatchInlineSnapshot(`"John Doe"`);
});
it('should match file snapshot', () => {
const html = renderComponent({ title: 'Test', count: 5 });
expect(html).toMatchFileSnapshot('./__snapshots__/component.html');
});
});

Testing React Components

typescript
import { describe, it, expect } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from './Button';
describe('Button component', () => {
it('should render with text', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
it('should handle click events', () => {
const onClick = vi.fn();
render(<Button onClick={onClick}>Click me</Button>);
fireEvent.click(screen.getByText('Click me'));
expect(onClick).toHaveBeenCalledOnce();
});
it('should be disabled when disabled prop is true', () => {
render(<Button disabled>Click me</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
it('should apply variant classes', () => {
render(<Button variant="primary">Primary</Button>);
expect(screen.getByRole('button')).toHaveClass('btn-primary');
});
});

Async Testing

typescript
import { describe, it, expect } from 'vitest';
describe('Async tests', () => {
it('should handle async/await', async () => {
const result = await fetchData();
expect(result.status).toBe('success');
});
it('should handle promises with resolves', async () => {
await expect(fetchUser('1')).resolves.toEqual({
id: '1',
name: 'Test User',
});
});
it('should handle promise rejections', async () => {
await expect(fetchUser('invalid')).rejects.toThrow('User not found');
});
it('should test multiple async operations', async () => {
const [user, posts] = await Promise.all([
fetchUser('1'),
fetchPosts('1'),
]);
expect(user.id).toBe('1');
expect(posts).toHaveLength(5);
});
});

Testing with Context and Fixtures

typescript
import { describe, it, expect, beforeEach } from 'vitest';
describe('User operations', () => {
let testUser: User;

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
118
Repository forks
6
7-day trend score
24

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 14 May 2026, 5 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/pramoddutta/qaskills/pramoddutta-vitest/badge)](https://verified-skill.com/skills/pramoddutta/qaskills/pramoddutta-vitest)

Something wrong with this skill or its page? Tell us.