askill
testing-typescript-vitest

testing-typescript-vitestSafety 100Repository

Set up and write tests using Vitest for TypeScript projects with proper configuration and TypeScript support

2 stars
1.2k downloads
Updated 2/8/2026

Package Files

Loading files...
SKILL.md

Skill: Testing TypeScript Vitest

Goal

Set up and write tests using Vitest for TypeScript projects with proper configuration, TypeScript support, and workspace conventions.

Use This Skill When

  • Setting up tests for a new TypeScript project
  • Writing tests using Vitest framework
  • Configuring Vitest for TypeScript
  • The user asks to "add Vitest tests" or "set up testing"

Do Not Use This Skill When

  • Project already has test framework configured
  • Using a different test framework (Ava, Jest)
  • Testing ClojureScript (use testing-clojure-cljs skill)

Vitest Setup

Package Dependencies

pnpm add -D vitest @vitest/ui @vitest/coverage-v8

Vitest Configuration

// vitest.config.ts
import { defineConfig } from 'vitest/config';
import tsconfigPaths from 'vite-tsconfig-paths';

export default defineConfig({
  test: {
    include: ['src/**/*.test.ts', 'src/**/*.test.tsx'],
    exclude: ['node_modules', 'dist', '.git'],
    
    // Coverage configuration
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
      exclude: [
        'node_modules/**',
        'dist/**',
        '**/*.d.ts',
        '**/*.test.ts',
        '**/*.config.ts'
      ]
    },
    
    // TypeScript setup
    globals: false, // Don't import vitest globals
    environment: 'node',
    
    // Setup files
    setupFiles: ['src/test/setup.ts'],
    
    // Thread options
    threads: true,
    isolate: true,
  },
  plugins: [tsconfigPaths()]
});

package.json Scripts

{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest",
    "test:ui": "vitest --ui",
    "test:coverage": "vitest run --coverage",
    "test:run": "vitest run --reporter=verbose"
  }
}

Test File Structure

src/
├── module.ts
├── module.test.ts        # Unit tests
├── module.integration.test.ts  # Integration tests
└── test/
    ├── setup.ts          # Test setup
    ├── mocks.ts          # Mock factories
    └── fixtures/         # Test fixtures

Basic Test Syntax

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { UserService } from './user.service';

describe('UserService', () => {
  let service: UserService;
  let mockRepository: any;

  beforeEach(() => {
    mockRepository = {
      findById: vi.fn(),
      create: vi.fn(),
      update: vi.fn(),
      delete: vi.fn()
    };
    service = new UserService(mockRepository);
  });

  describe('findById', () => {
    it('returns user when found', async () => {
      const mockUser = { id: '123', name: 'Test' };
      mockRepository.findById.mockResolvedValue(mockUser);

      const result = await service.findById('123');

      expect(result).toEqual(mockUser);
      expect(mockRepository.findById).toHaveBeenCalledWith('123');
    });

    it('returns null when not found', async () => {
      mockRepository.findById.mockResolvedValue(null);

      const result = await service.findById('non-existent');

      expect(result).toBeNull();
    });
  });

  describe('create', () => {
    it('creates and returns new user', async () => {
      const input = { name: 'New User', email: 'new@example.com' };
      const created = { id: '456', ...input };
      mockRepository.create.mockResolvedValue(created);

      const result = await service.create(input);

      expect(result).toEqual(created);
      expect(mockRepository.create).toHaveBeenCalledWith(input);
    });
  });
});

TypeScript Features

Generic Fixtures

// test/fixtures/user.fixture.ts
export function createUserFixture(overrides = {}) {
  return {
    id: 'user-123',
    name: 'Test User',
    email: 'test@example.com',
    role: 'user',
    createdAt: new Date('2024-01-01'),
    ...overrides
  };
}

export type UserFixture = ReturnType<typeof createUserFixture>;

// In tests
import { createUserFixture } from '../fixtures/user.fixture';

it('validates user email', () => {
  const user = createUserFixture({ email: 'invalid-email' });
  expect(validateEmail(user.email)).toBe(false);
});

Mocking with vi

import { vi, describe, it, expect } from 'vitest';

// Mock module
vi.mock('./api/client');

// Get mocked imports
import { fetchUser, updateUser } from './api/client';

describe('User API', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('fetches user with retry', async () => {
    fetchUser.mockRejectedValueOnce(new Error('Network error'));
    fetchUser.mockResolvedValue({ id: '123', name: 'Test' });

    const result = await fetchUserWithRetry('123');

    expect(result).toEqual({ id: '123', name: 'Test' });
    expect(fetchUser).toHaveBeenCalledTimes(2);
  });
});

Spy on Objects

describe('Service with callbacks', () => {
  it('calls onComplete callback', () => {
    const onComplete = vi.fn();
    const service = new ProcessingService({ onComplete });

    service.process();

    expect(onComplete).toHaveBeenCalledTimes(1);
    expect(onComplete).toHaveBeenCalledWith({ status: 'complete' });
  });

  it('calls callback with correct arguments', () => {
    const callback = vi.fn();
    const processor = new BatchProcessor(callback);

    processor.run([1, 2, 3]);

    expect(callback).toHaveBeenCalledWith(3); // Final result
  });
});

Async Testing

import { describe, it, expect, vi } from 'vitest';

describe('Async Operations', () => {
  describe('promises', () => {
    it('resolves with value', async () => {
      const result = await Promise.resolve('success');
      expect(result).toBe('success');
    });

    it('rejects with error', async () => {
      await expect(Promise.reject(new Error('fail')))
        .rejects
        .toThrow('fail');
    });
  });

  describe('timers', () => {
    it('handles setTimeout', async () => {
      vi.useFakeTimers();
      const callback = vi.fn();
      
      setTimeout(callback, 1000);
      
      // Advance timers
      vi.advanceTimersByTime(1000);
      
      expect(callback).toHaveBeenCalled();
    });
  });

  describe('concurrent', () => {
    it('handles Promise.all', async () => {
      const results = await Promise.all([
        Promise.resolve(1),
        Promise.resolve(2),
        Promise.resolve(3)
      ]);
      
      expect(results).toEqual([1, 2, 3]);
    });

    it('handles Promise.allSettled', async () => {
      const results = await Promise.allSettled([
        Promise.resolve('success'),
        Promise.reject('fail')
      ]);
      
      expect(results[0].status).toBe('fulfilled');
      expect(results[1].status).toBe('rejected');
    });
  });
});

Test Setup File

// src/test/setup.ts
import { beforeEach, afterEach, vi } from 'vitest';

// Reset mocks before each test
beforeEach(() => {
  vi.clearAllMocks();
  vi.restoreAllMocks();
});

// Clean up after all tests
afterAll(() => {
  vi.clearAllTimers();
});

// Global test utilities
globalThis.testUtils = {
  createMockUser: (overrides = {}) => ({
    id: 'test-id',
    name: 'Test User',
    email: 'test@example.com',
    ...overrides
  }),
  
  wait: (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
};

Type-Safe Mocks

// test/mocks/repository.mock.ts
import { vi } from 'vitest';
import type { UserRepository } from '../../src/user.repository';

export function createMockRepository(): UserRepository {
  return {
    findById: vi.fn(),
    findByEmail: vi.fn(),
    create: vi.fn(),
    update: vi.fn(),
    delete: vi.fn(),
    findAll: vi.fn()
  };
}

// In test
import { createMockRepository } from '../mocks/repository.mock';

it('uses repository methods', () => {
  const repo = createMockRepository();
  const service = new UserService(repo);
  
  service.getById('123');
  
  expect(repo.findById).toHaveBeenCalledWith('123');
});

Workspace Conventions

Naming

  • Unit tests: *.test.ts
  • Integration tests: *.integration.test.ts
  • E2E tests: tests/e2e/*.test.ts

Location

src/
├── component.ts
├── component.test.ts         # Unit tests
└── __tests__/
    └── integration.test.ts   # Integration tests

Assertions

Use descriptive assertions:

// GOOD
expect(user.id).toBeDefined();
expect(users).toHaveLength(3);
expect(result).toEqual(expected);

// AVOID
expect(user.id).toBeTruthy();
expect(users.length).toBe(3);
expect(JSON.stringify(result)).toBe(JSON.stringify(expected));

Output

  • Vitest configuration file (vitest.config.ts)
  • Test setup file with utilities
  • Example test files with TypeScript
  • Mock factories and fixtures
  • Updated package.json scripts

References

Install

Download ZIP
Requires askill CLI v1.0+

AI Quality Score

95/100Analyzed 2/9/2026

A comprehensive and highly actionable guide for setting up and using Vitest in TypeScript projects, covering configuration, mocking, async testing, and workspace conventions.

100
95
92
98
96

Metadata

Licenseunknown
Version-
Updated2/8/2026
Publisherriatzukiza

Tags

apitesting