typescript-testing
SKILL.md
TypeScript Testing Skill
You are a testing specialist for TypeScript projects.
Testing Frameworks
Framework Detection
jest.config.*or"jest"in package.json → Jestvitest.config.*or"vitest"in package.json → Vitestcypress.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 () => {
// Arrange
const expectedUser = { id: '1', name: 'Test' };
mockRepository.findById.mockResolvedValue(expectedUser);
// Act
const result = await sut.getUser('1');
// Assert
expect(result).toEqual(expectedUser);
expect(mockRepository.findById).toHaveBeenCalledWith('1');
});
it('should return null when user not found', async () => {
// Arrange
mockRepository.findById.mockResolvedValue(null);
// Act
const result = await sut.getUser('unknown');
// Assert
expect(result).toBeNull();
});
});
});
Mocking Strategies
// Mock modules
jest.mock('./database', () => ({
getConnection: jest.fn().mockResolvedValue(mockConnection),
}));
// Mock implementations (include standard Response properties)
const mockData = { id: '1', name: 'Test' };
const mockFetch = jest.fn().mockImplementation((url: string) =>
Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockData) })
);
// Spy on methods
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
// Testing async functions
it('should handle async errors', async () => {
mockApi.get.mockRejectedValue(new NetworkError('timeout'));
await expect(service.fetchData('url')).rejects.toThrow(NetworkError);
});
// Testing timers (clean up in afterEach to prevent leaks on failure)
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
.envfiles or API keys in tests - Use test fixtures for complex data structures
Weekly Installs
9
Repository
dmitriyyukhanov…-pluginsGitHub Stars
2
First Seen
12 days ago
Security Audits
Installed on
cline9
github-copilot9
codex9
kimi-cli9
gemini-cli9
cursor9