| name | typescript-testing |
| description | TypeScript testing patterns with Jest/Vitest including unit tests, integration tests, mocking strategies, and coverage. Use when writing TypeScript tests. |
TypeScript Testing Skill
You are a testing specialist for TypeScript projects.
Testing Frameworks
Framework Detection
jest.config.* or "jest" in package.json → Jest
vitest.config.* or "vitest" in package.json → Vitest
cypress.config.* → Cypress (E2E)
playwright.config.* → Playwright (E2E)
- If both Jest and Vitest are present, follow the scripts used by CI and existing test files in the target package
Test Distribution
- ~75% Unit Tests: Fast, isolated, fully mocked
- ~20% Integration Tests: Module interactions, API contracts
- ~5% E2E Tests: Full user flows (Cypress/Playwright)
Unit Test Patterns
Examples below use Jest APIs. For Vitest, replace jest with vi and import helpers from vitest.
Arrange-Act-Assert
describe('UserService', () => {
let sut: UserService;
let mockRepository: jest.Mocked<IUserRepository>;
beforeEach(() => {
mockRepository = {
findById: jest.fn(),
save: jest.fn(),
};
sut = new UserService(mockRepository);
});
afterEach(() => {
jest.clearAllMocks();
});
describe('getUser', () => {
it('should return user when found', async () => {
const expectedUser = { id: '1', name: 'Test' };
mockRepository.findById.mockResolvedValue(expectedUser);
const result = await sut.getUser('1');
expect(result).toEqual(expectedUser);
expect(mockRepository.findById).toHaveBeenCalledWith('1');
});
it('should return null when user not found', async () => {
mockRepository.findById.mockResolvedValue(null);
const result = await sut.getUser('unknown');
expect(result).toBeNull();
});
});
});
Mocking Strategies
jest.mock('./database', () => ({
getConnection: jest.fn().mockResolvedValue(mockConnection),
}));
const mockData = { id: '1', name: 'Test' };
const mockFetch = jest.fn().mockImplementation((url: string) =>
Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockData) })
);
const spy = jest.spyOn(service, 'validate');
expect(spy).toHaveBeenCalledTimes(1);
Integration Test Patterns
describe('API Integration', () => {
let app: Express;
beforeAll(async () => {
app = await createApp({ database: testDb });
});
afterAll(async () => {
await testDb.close();
});
it('should create and retrieve user', async () => {
const createResponse = await request(app)
.post('/users')
.send({ name: 'Test', email: 'test@example.com' })
.expect(201);
const getResponse = await request(app)
.get(`/users/${createResponse.body.id}`)
.expect(200);
expect(getResponse.body.name).toBe('Test');
});
});
React Component Testing
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
describe('Button', () => {
it('should call onClick when clicked', async () => {
const user = userEvent.setup();
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
await user.click(screen.getByText('Click me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it('should be disabled when loading', () => {
render(<Button isLoading>Submit</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
});
Async Testing
it('should handle async errors', async () => {
mockApi.get.mockRejectedValue(new NetworkError('timeout'));
await expect(service.fetchData('url')).rejects.toThrow(NetworkError);
});
describe('debounce', () => {
beforeEach(() => jest.useFakeTimers());
afterEach(() => jest.useRealTimers());
it('should debounce input', () => {
const callback = jest.fn();
debounce(callback, 300)('test');
expect(callback).not.toHaveBeenCalled();
jest.advanceTimersByTime(300);
expect(callback).toHaveBeenCalledWith('test');
});
});
Vitest Equivalents
If using Vitest instead of Jest, the API is nearly identical:
| Jest | Vitest |
|---|
jest.fn() | vi.fn() |
jest.mock() | vi.mock() |
jest.spyOn() | vi.spyOn() |
jest.clearAllMocks() | vi.clearAllMocks() |
jest.useFakeTimers() | vi.useFakeTimers() |
jest.Mocked<T> | Mocked<T> (from vitest) |
Coverage Guidelines
- Enforce ≥80% coverage on critical business logic
- Don't chase 100% - focus on meaningful tests
- Never commit real
.env files or API keys in tests
- Use test fixtures for complex data structures