К основному содержанию
правило

vitest — правило для Cursor: тесты на Vitest без флаки

Правило задаёт Cursor, как писать юнит- и интеграционные тесты на Vitest: где держать файлы, как изолировать кейсы и мокать модули.

Установить в Cursor · Вручную: положить файлы Все способы и платформы ↓
.cursor/rules/vitest.mdc
Открыть на GitHub

Что делает vitest

Правило описывает рабочие приёмы для проектов на Vite, где тесты пишут на Vitest. Файлы тестов советует класть рядом с исходником и называть по шаблону *.test.{ts,tsx,js,jsx} — тогда при правке компонента тест виден сразу, а не теряется в отдельной папке.

Изоляция кейсов держится на beforeEach и afterEach: свежие данные создаются перед тестом, удаляются после. Для асинхронных проверок правило требует vi.waitFor вместо setTimeout — именно ручные паузы дают флаки на медленной машине или в CI. Моки строятся на vi.fn(), vi.spyOn() и vi.mock(), а vi.clearAllMocks() в afterEach не даёт состоянию протечь в следующий тест.

Для компонентов рекомендуется окружение happy-dom в vite.config.ts или vitest.config.ts, а jsdom — только если нужного браузерного API в happy-dom нет. Независимые кейсы можно пометить .concurrent, но тогда expect придётся доставать из контекста теста, иначе сбоят снапшоты и учёт проверок.

Файл кладут в .cursor/rules/vitest.mdc, он подходит под globs **/*.{js,ts,jsx,tsx}.

Какие задачи решает

  • договориться, где лежат файлы тестов и как их называть
  • убрать флаки из асинхронных проверок через vi.waitFor
  • изолировать кейсы, чтобы состояние не перетекало между тестами
  • замокать модуль целиком и подменить одну функцию внутри
  • ускорить прогон независимых тестов через .concurrent

Примеры запросов агенту

  • напиши тесты на Vitest для этого модуля, файл положи рядом с исходником
  • перепиши этот тест с setTimeout на vi.waitFor
  • замокай модуль lodash и проверь, что debounce вызван один раз

Как установить vitest

Вручную: положить файлы

.cursor/rules/vitest.mdc

Во frontmatter: description, globs, alwaysApply

По шагам, с командами для каждой платформы: Cursor rules: файлы .mdc, четыре режима и замена .cursorrules

Вопросы о vitest

Как установить правило vitest в Cursor?

Скопируйте файл в .cursor/rules/vitest.mdc в корне проекта. Cursor подхватит его для файлов, попадающих под globs **/*.{js,ts,jsx,tsx}.

Что делает это правило?

Задаёт агенту набор приёмов для тестов на Vitest: co-location тестовых файлов, изоляцию через хуки, vi.waitFor для асинхронных условий, моки через vi и окружение happy-dom.

Обязательно ли переходить на happy-dom?

Правило советует happy-dom как более быстрый вариант для большинства компонентных тестов, а jsdom оставляет на случай, когда нужного браузерного API в happy-dom нет.

Исходный текст

vitest.mdc · CC0-1.0
---
description: This guide provides opinionated, actionable best practices for writing fast, reliable, and maintainable unit and integration tests using Vitest in modern JavaScript and TypeScript projects.
globs: **/*.{js,ts,jsx,tsx}
---
# Vitest Best Practices

Vitest is the definitive testing framework for our Vite-powered projects. It offers a fast, Jest-compatible API with deep integration into the Vite ecosystem. Adhering to these guidelines ensures our tests are robust, performant, and easy to maintain.

## 1. Code Organization & Naming

**Always co-locate test files with their source.** This improves discoverability and ensures tests are updated alongside their implementation.

*   **File Naming**: Use `*.test.{ts,tsx,js,jsx}`.
*   **Location**: Place test files directly next to the component or module they test.

❌ BAD:
```
// src/components/Button/Button.tsx
// tests/components/Button.test.tsx
```

✅ GOOD:
```typescript
// src/components/Button/Button.tsx
// src/components/Button/Button.test.tsx
```

## 2. Test Structure & Isolation

**Organize tests logically using `describe` and `it` (or `test`) blocks.** Ensure each test is isolated and deterministic.

*   **`describe`**: Group related tests into suites.
*   **`it` / `test`**: Define individual test cases. Prefer `it` for consistency with Jest.
*   **Hooks (`beforeEach`, `afterEach`)**: Use these for setup and teardown to ensure test isolation.

❌ BAD: (Shared state, no cleanup)
```typescript
let user;
test('creates user', () => {
  user = createUser();
  expect(user).toBeDefined();
});
test('updates user', () => { // Depends on previous test
  user.name = 'New Name';
  expect(user.name).toBe('New Name');
});
```

✅ GOOD: (Isolated tests with hooks)
```typescript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { createUser, deleteUser } from './user-service';

describe('User Service', () => {
  let user;

  beforeEach(() => {
    user = createUser(); // Create a fresh user for each test
  });

  afterEach(() => {
    deleteUser(user.id); // Clean up after each test
  });

  it('should create a user', () => {
    expect(user).toBeDefined();
    expect(user.id).toBeTypeOf('string');
  });

  it('should update a user', () => {
    user.name = 'Jane Doe';
    expect(user.name).toBe('Jane Doe');
  });
});
```

## 3. Asynchronous Testing with `vi.waitFor`

**Always use `vi.waitFor` for polling conditions in asynchronous tests.** Avoid arbitrary `setTimeout` calls or manual polling loops. `vi.waitFor` is designed for reliable synchronization.

❌ BAD: (Flaky, relies on arbitrary timeout)
```typescript
test('data loads after delay', async () => {
  let data = null;
  fetchData().then(res => (data = res));
  await new Promise(resolve => setTimeout(resolve, 100)); // Arbitrary wait
  expect(data).toEqual('some data');
});
```

✅ GOOD: (Reliable polling with `vi.waitFor`)
```typescript
import { it, expect, vi } from 'vitest';
import { fetchData } from './api'; // Assume fetchData returns a Promise

it('should load data after delay', async () => {
  let data = null;
  fetchData().then(res => (data = res));

  // Polls until data is not null, with a 2-second timeout
  await vi.waitFor(() => expect(data).not.toBeNull(), { timeout: 2000 });

  expect(data).toEqual('some data');
});
```

## 4. Mocking Strategies

**Leverage Vitest's `vi` API for all mocking.** This provides Jest-compatible syntax and seamless integration. Always clean up mocks after each test.

*   **`vi.fn()`**: Mock individual functions.
*   **`vi.spyOn()`**: Spy on existing object methods.
*   **`vi.mock()`**: Mock entire modules.

### Function Mocking

❌ BAD: (Manual mock, no easy reset)
```typescript
const originalFetch = global.fetch;
global.fetch = () => Promise.resolve({ json: () => ({ id: 1 }) });
// ... test ...
global.fetch = originalFetch; // Easy to forget cleanup
```

✅ GOOD: (Using `vi.fn` with `afterEach` cleanup)
```typescript
import { it, expect, vi, afterEach } from 'vitest';
import { getUser } from './user-api';

// Mock the module containing fetchUser
vi.mock('./user-api', async (importOriginal) => {
  const mod = await importOriginal();
  return {
    ...mod,
    fetchUser: vi.fn(), // Mock specific function within the module
  };
});

// Import the mocked function after vi.mock
import { fetchUser } from './user-api';

afterEach(() => {
  vi.clearAllMocks(); // Clear mock calls after each test to prevent state leakage
});

it('should fetch user data', async () => {
  fetchUser.mockResolvedValueOnce({ id: 1, name: 'Test User' });
  const user = await getUser(1);
  expect(fetchUser).toHaveBeenCalledWith(1);
  expect(user.name).toBe('Test User');
});
```

### Module Mocking

**Mock modules at the top of the file.** This ensures the mock is applied before the module under test imports it.

✅ GOOD: (Module mock before imports)
```typescript
import { vi, it, expect } from 'vitest';

// Mock the entire 'lodash' module to control its behavior
vi.mock('lodash', () => ({
  debounce: vi.fn((fn) => fn), // Mock debounce to execute immediately
}));

import { debounce } from 'lodash'; // Import the mocked debounce
import { saveInput } from './input-handler'; // Module using debounce

it('should call save function without debounce delay', () => {
  saveInput('test');
  expect(debounce).toHaveBeenCalledOnce();
});
```

## 5. DOM Environment & Component Testing

**Use `happy-dom` for lightweight DOM environments.** It's generally faster and sufficient for most component tests. Switch to `jsdom` only if specific browser APIs are missing in `happy-dom`.

*   Configure in `vite.config.ts` or `vitest.config.ts`.

✅ GOOD: (Configuring `happy-dom`)
```typescript
// vite.config.ts or vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    environment: 'happy-dom', // Use happy-dom for faster DOM mocking
    globals: true, // Auto-import test APIs globally (e.g., describe, it, expect)
  },
});
```

## 6. Performance & Concurrent Tests

**Utilize `.concurrent` for tests that can run in parallel.** This significantly speeds up test suites where tests are independent.

*   Use `it.concurrent` for individual tests.
*   Use `describe.concurrent` for entire suites.
*   **Important**: When using `.concurrent`, always destructure `expect` from the test context to avoid issues with snapshot and assertion tracking.

❌ BAD: (Sequential tests, slow)
```typescript
describe('My Feature', () => {
  it('test A', async () => { /* ... */ });
  it('test B', async () => { /* ... */ });
});
```

✅ GOOD: (Concurrent tests, faster)
```typescript
import { describe, it } from 'vitest';

describe.concurrent('My Feature', () => {
  it('test A', async ({ expect }) => { // Destructure expect for concurrent tests
    expect(1).toBe(1);
  });

  it.concurrent('test B', async ({ expect }) => { // Destructure expect
    expect(2).toBe(2);
  });
});
```

## 7. Code Coverage

**Enable V8-based code coverage.** It offers near-zero overhead and integrates seamlessly.

*   Add `coverage` configuration to `vite.config.ts` or `vitest.config.ts`.
*   Run with `vitest run --coverage`.

✅ GOOD: (V8 coverage configuration)
```typescript
// vite.config.ts or vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    environment: 'happy-dom',
    globals: true,
    coverage: {
      provider: 'v8', // Use V8 for native, fast coverage
      reporter: ['text', 'json', 'html'], // Output formats for reports
      exclude: ['node_modules/', 'dist/', '.eslintrc.cjs'], // Exclude common directories from coverage
    },
  },
});
```

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Похожие правила IDE

Всё по теме «Тесты и QA» →
vitest-testing secondsky Скилл для написания юнит- и интеграционных тестов на Vitest в проектах на TypeScript и JavaScript. Пригодится, когда нужно быстро настроить конфиг, моки и покрытие. CX Codex +1 1 200установок vitest-skill TestMu AI Генерирует юнит-тесты на Vitest для JavaScript и TypeScript проектов. Помогает настроить моки через vi и протестировать компоненты React. CX Codex +1 448установок vitest-midscene-e2e web-infra-dev Скилл объединяет Vitest и Midscene для создания E2E-тестов на естественном языке под Web, Android и iOS. Помогает быстро развернуть бойлерплейт или наладить автотесты без селекторов. CX Codex +1 2 100установок unit-test-vue-pinia официальный github Помогает писать и проверять юнит-тесты для Vue 3, TypeScript, Vitest и Pinia. Пригодится, когда тесты уже есть, но лезут во внутренности компонентов. 1 800установок testing-best-practices agent-skills Набор правил по юнит- и интеграционным тестам и TDD: 34 правила в 7 категориях на TypeScript с Jest и Vitest. Пригодится, когда пишете тесты или разбираете чужие. CX Codex +1 899установок

Из того же набора69

zod Правило для Cursor задаёт стандарты работы с Zod: строгий режим TypeScript, вывод типов из схем и проверка данных на входе. ★ 3 579звёзд репо vercel Правило для Cursor с набором рекомендаций по разработке и деплою приложений на Vercel. Пригодится, если агент пишет код для Next.js и вы хотите, чтобы он сразу держал в голове конвенции платформы. ★ 3 579звёзд репо unittest Правило задаёт агенту стандарт оформления тестов на unittest: структура каталогов, имена классов и методов, ассерты, моки. ★ 3 579звёзд репо transformers Правило для Cursor задаёт стиль работы с библиотекой 🤗 Transformers: модульные модели, типизация, Trainer вместо ручных циклов обучения. ★ 3 579звёзд репо tornado Правило для Cursor задаёт стиль кода в Tornado: только async/await для ввода-вывода, полные аннотации типов и один обработчик ошибок на всё приложение. ★ 3 579звёзд репо three-js Правило для Cursor задаёт стандарты разработки на three.js: обработка ошибок WebGL, ленивая загрузка ассетов, оптимизация цикла рендера и освобождение ресурсов. ★ 3 579звёзд репо svelte Правило для Cursor задаёт, как писать компоненты Svelte 5: руны вместо старой реактивности, типизация и ключи в списках. Пригодится, если проект уже на Svelte 5. ★ 3 579звёзд репо streamlit Правило для Cursor задаёт, как писать приложения на Streamlit: кэшировать тяжёлые операции, держать UI отдельно от логики и разбивать проект на модули. ★ 3 579звёзд репо

Спонсоры проекта

Компании, которые помогают держать аналитику открытой