home / skills / dexploarer / claudius-skills / testing-framework-helper
/examples/intermediate/framework-skills/testing-framework-helper
This skill generates comprehensive test suites across unit, integration, and end-to-end levels for multiple frameworks to speed up robust testing.
npx playbooks add skill dexploarer/claudius-skills --skill testing-framework-helperReview the files below or copy the command above to add this skill to your agents.
---
name: testing-framework-helper
description: Generates comprehensive test suites with unit tests, integration tests, and E2E tests for various frameworks (Jest, Pytest, Vitest, etc.). Use when writing tests.
---
# Testing Framework Helper Skill
Expert at creating comprehensive test suites following testing best practices.
## When to Activate
- "write tests for [component/function]"
- "create test suite for [feature]"
- "generate unit/integration/E2E tests"
## Jest/Vitest (JavaScript/TypeScript)
```typescript
// UserService.test.ts
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { UserService } from './UserService';
import { mockDatabase } from '../test-utils/mockDatabase';
describe('UserService', () => {
let userService: UserService;
let db: ReturnType<typeof mockDatabase>;
beforeEach(() => {
db = mockDatabase();
userService = new UserService(db);
});
afterEach(() => {
vi.clearAllMocks();
});
describe('getById', () => {
it('should return user when found', async () => {
const mockUser = {
id: 1,
name: 'John Doe',
email: '[email protected]',
};
db.user.findUnique.mockResolvedValue(mockUser);
const result = await userService.getById(1);
expect(result).toEqual(mockUser);
expect(db.user.findUnique).toHaveBeenCalledWith({
where: { id: 1 },
});
});
it('should return null when user not found', async () => {
db.user.findUnique.mockResolvedValue(null);
const result = await userService.getById(999);
expect(result).toBeNull();
});
it('should throw error on database failure', async () => {
db.user.findUnique.mockRejectedValue(new Error('DB Error'));
await expect(userService.getById(1)).rejects.toThrow('DB Error');
});
});
describe('create', () => {
it('should create user with valid data', async () => {
const userData = {
name: 'Jane Doe',
email: '[email protected]',
password: 'SecurePass123!',
};
const createdUser = {
id: 2,
...userData,
password: 'hashed_password',
};
db.user.create.mockResolvedValue(createdUser);
const result = await userService.create(userData);
expect(result).toEqual(createdUser);
expect(db.user.create).toHaveBeenCalledWith({
data: expect.objectContaining({
name: userData.name,
email: userData.email,
}),
});
});
it('should throw error when email exists', async () => {
const userData = {
name: 'John Doe',
email: '[email protected]',
password: 'pass123',
};
db.user.findUnique.mockResolvedValue({ id: 1 });
await expect(userService.create(userData)).rejects.toThrow(
'Email already exists'
);
});
});
});
```
## React Testing Library
```typescript
// UserList.test.tsx
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { UserList } from './UserList';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const mockUsers = [
{ id: 1, name: 'Alice', email: '[email protected]' },
{ id: 2, name: 'Bob', email: '[email protected]' },
];
const createWrapper = () => {
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
return ({ children }) => (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
};
describe('UserList', () => {
beforeEach(() => {
global.fetch = vi.fn();
});
it('renders loading state initially', () => {
(global.fetch as any).mockImplementation(() => new Promise(() => {}));
render(<UserList />, { wrapper: createWrapper() });
expect(screen.getByText(/loading/i)).toBeInTheDocument();
});
it('renders user list after loading', async () => {
(global.fetch as any).mockResolvedValueOnce({
ok: true,
json: async () => mockUsers,
});
render(<UserList />, { wrapper: createWrapper() });
await waitFor(() => {
expect(screen.getByText('Alice')).toBeInTheDocument();
expect(screen.getByText('Bob')).toBeInTheDocument();
});
});
it('handles search functionality', async () => {
render(<UserList />, { wrapper: createWrapper() });
const searchInput = screen.getByPlaceholderText(/search/i);
fireEvent.change(searchInput, { target: { value: 'Alice' } });
await waitFor(() => {
expect(global.fetch).toHaveBeenCalledWith(
expect.stringContaining('search=Alice')
);
});
});
it('handles error state', async () => {
(global.fetch as any).mockRejectedValueOnce(new Error('API Error'));
render(<UserList />, { wrapper: createWrapper() });
await waitFor(() => {
expect(screen.getByText(/error/i)).toBeInTheDocument();
});
});
});
```
## Pytest (Python)
```python
# test_user_service.py
import pytest
from unittest.mock import Mock, patch, AsyncMock
from app.services.user_service import UserService
from app.models.user import User
from app.exceptions import ValidationError, NotFoundError
@pytest.fixture
def user_service():
"""Fixture for UserService instance"""
return UserService()
@pytest.fixture
def mock_user():
"""Fixture for mock user data"""
return User(
id=1,
email="[email protected]",
name="Test User",
role="user"
)
class TestUserService:
@pytest.mark.asyncio
async def test_get_by_id_success(self, user_service, mock_user):
"""Test getting user by ID successfully"""
with patch.object(User, 'get', return_value=mock_user) as mock_get:
result = await user_service.get_by_id(1)
assert result == mock_user
mock_get.assert_called_once_with(id=1)
@pytest.mark.asyncio
async def test_get_by_id_not_found(self, user_service):
"""Test getting non-existent user"""
with patch.object(User, 'get', return_value=None):
with pytest.raises(NotFoundError, match="User not found"):
await user_service.get_by_id(999)
@pytest.mark.asyncio
async def test_create_success(self, user_service):
"""Test creating user successfully"""
user_data = {
"email": "[email protected]",
"name": "New User",
"password": "SecurePass123!"
}
with patch.object(User, 'create', return_value=Mock(id=2)) as mock_create:
result = await user_service.create(user_data)
assert result.id == 2
mock_create.assert_called_once()
@pytest.mark.asyncio
async def test_create_duplicate_email(self, user_service, mock_user):
"""Test creating user with existing email"""
user_data = {
"email": "[email protected]",
"name": "Test",
"password": "pass123"
}
with patch.object(User, 'get_by_email', return_value=mock_user):
with pytest.raises(ValidationError, match="Email already exists"):
await user_service.create(user_data)
@pytest.mark.parametrize("email,valid", [
("[email protected]", True),
("invalid-email", False),
("", False),
("test@test", False),
])
def test_validate_email(self, user_service, email, valid):
"""Test email validation with various inputs"""
if valid:
user_service.validate_email(email)
else:
with pytest.raises(ValidationError):
user_service.validate_email(email)
```
## Integration Tests
```typescript
// user.integration.test.ts
import request from 'supertest';
import { app } from '../app';
import { setupTestDB, cleanupTestDB, seedTestData } from '../test-utils';
describe('User API Integration Tests', () => {
let authToken: string;
beforeAll(async () => {
await setupTestDB();
const seedData = await seedTestData();
authToken = seedData.adminToken;
});
afterAll(async () => {
await cleanupTestDB();
});
describe('GET /api/users', () => {
it('should return paginated users', async () => {
const response = await request(app)
.get('/api/users')
.set('Authorization', `Bearer ${authToken}`)
.query({ page: 1, limit: 10 })
.expect(200);
expect(response.body).toHaveProperty('users');
expect(response.body).toHaveProperty('meta');
expect(Array.isArray(response.body.users)).toBe(true);
expect(response.body.meta).toMatchObject({
page: 1,
limit: 10,
});
});
it('should require authentication', async () => {
await request(app)
.get('/api/users')
.expect(401);
});
});
describe('POST /api/users', () => {
it('should create new user with valid data', async () => {
const userData = {
name: 'New User',
email: '[email protected]',
password: 'SecurePass123!',
};
const response = await request(app)
.post('/api/users')
.send(userData)
.expect(201);
expect(response.body.data).toMatchObject({
name: userData.name,
email: userData.email,
});
expect(response.body.data).not.toHaveProperty('password');
});
it('should reject invalid email', async () => {
const userData = {
name: 'Test',
email: 'invalid-email',
password: 'pass123',
};
const response = await request(app)
.post('/api/users')
.send(userData)
.expect(400);
expect(response.body).toHaveProperty('error');
});
});
});
```
## E2E Tests (Playwright/Cypress)
```typescript
// e2e/user-management.spec.ts
import { test, expect } from '@playwright/test';
test.describe('User Management', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/login');
await page.fill('[name="email"]', '[email protected]');
await page.fill('[name="password"]', 'admin123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
});
test('should display user list', async ({ page }) => {
await page.goto('/users');
await expect(page.locator('h1')).toContainText('Users');
await expect(page.locator('[data-testid="user-row"]')).toHaveCount(10);
});
test('should create new user', async ({ page }) => {
await page.goto('/users');
await page.click('[data-testid="add-user-button"]');
await page.fill('[name="name"]', 'Test User');
await page.fill('[name="email"]', '[email protected]');
await page.fill('[name="password"]', 'SecurePass123!');
await page.click('[data-testid="submit-button"]');
await expect(page.locator('.success-message')).toBeVisible();
await expect(page.locator('text=Test User')).toBeVisible();
});
test('should search users', async ({ page }) => {
await page.goto('/users');
await page.fill('[data-testid="search-input"]', 'Alice');
await page.waitForLoadState('networkidle');
const rows = page.locator('[data-testid="user-row"]');
await expect(rows).toHaveCount(1);
await expect(rows.first()).toContainText('Alice');
});
});
```
## Best Practices
- Follow AAA pattern (Arrange, Act, Assert)
- Use descriptive test names
- Test edge cases and error conditions
- Mock external dependencies
- Use fixtures for test data
- Clean up after tests
- Test both happy and unhappy paths
- Use parameterized tests for similar cases
- Maintain test independence
- Keep tests fast
- Use proper assertion libraries
- Test accessibility
- Use test coverage tools
- Avoid test interdependence
## Output Checklist
- ✅ Test file created
- ✅ Setup/teardown configured
- ✅ Unit tests written
- ✅ Integration tests (if needed)
- ✅ E2E tests (if needed)
- ✅ Mocks/fixtures created
- ✅ Edge cases covered
- 📝 Test documentation
This skill generates comprehensive test suites across unit, integration, and end-to-end testing frameworks. It produces ready-to-run tests for Jest, Vitest, Pytest, Playwright, Cypress, and integration tools like Supertest. The focus is on practical, maintainable test code with fixtures, mocks, and setup/teardown already configured.
Provide the component, function, or feature to test and the preferred frameworks and scope (unit/integration/E2E). The skill analyzes typical patterns and produces files with Arrange-Act-Assert structure, descriptive test names, and edge-case coverage. It includes mocks for external dependencies, fixtures for test data, and recommended setup/teardown hooks and CI-friendly configuration.
Can it create tests for multiple frameworks at once?
Yes. Specify the frameworks you need and it will generate matching suites and notes on differences.
Does it include setup for test databases and seeds?
It provides setup/teardown hooks and example utilities for test DB setup, seeding, and cleanup tailored to the chosen stack.
Will generated tests mock external APIs and services?
Yes. The skill adds mocks or stubs for external dependencies and shows how to inject test doubles.