plugins/backend-and-infra/skills/automating-tests/SKILL.md
Create comprehensive test suites with unit, integration, and e2e tests. Use when writing tests, setting up test automation, creating test fixtures, configuring CI test pipelines, or improving test coverage.
npx skillsauth add arosenkranz/claude-code-config automating-testsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Build comprehensive test suites following the test pyramid pattern.
/\
/E2E\ <- Few, slow, expensive
/------\
/ INTEG \ <- Some, medium speed
/----------\
/ UNIT \ <- Many, fast, cheap
--------------
Focus: Many unit tests, fewer integration tests, minimal E2E tests.
// Jest example
describe('UserService', () => {
describe('createUser', () => {
it('creates user with valid data', async () => {
// Arrange
const service = new UserService();
const userData = {
email: '[email protected]',
name: 'Test User'
};
// Act
const user = await service.createUser(userData);
// Assert
expect(user).toMatchObject(userData);
expect(user.id).toBeDefined();
expect(user.createdAt).toBeInstanceOf(Date);
});
it('throws ValidationError for invalid email', async () => {
// Arrange
const service = new UserService();
// Act & Assert
await expect(
service.createUser({ email: 'invalid', name: 'Test' })
).rejects.toThrow(ValidationError);
});
});
});
import pytest
from user_service import UserService, ValidationError
class TestUserService:
@pytest.fixture
def service(self):
"""Provide fresh UserService for each test."""
return UserService()
def test_create_user_with_valid_data(self, service):
# Arrange
user_data = {
'email': '[email protected]',
'name': 'Test User'
}
# Act
user = service.create_user(user_data)
# Assert
assert user.email == user_data['email']
assert user.name == user_data['name']
assert user.id is not None
def test_create_user_invalid_email(self, service):
# Act & Assert
with pytest.raises(ValidationError, match="Invalid email"):
service.create_user({'email': 'invalid', 'name': 'Test'})
@pytest.mark.parametrize('email,should_pass', [
('[email protected]', True),
('[email protected]', True),
('invalid', False),
('@example.com', False),
('user@', False),
])
def test_email_validation(self, service, email, should_pass):
if should_pass:
user = service.create_user({'email': email, 'name': 'Test'})
assert user.email == email
else:
with pytest.raises(ValidationError):
service.create_user({'email': email, 'name': 'Test'})
import { fetchUserData } from './api';
import { processUser } from './user-processor';
jest.mock('./api');
describe('processUser', () => {
it('processes user data from API', async () => {
// Mock the API call
fetchUserData.mockResolvedValue({
id: 1,
name: 'John',
email: '[email protected]'
});
const result = await processUser(1);
expect(fetchUserData).toHaveBeenCalledWith(1);
expect(result.name).toBe('JOHN'); // uppercase transformation
});
it('handles API errors', async () => {
fetchUserData.mockRejectedValue(new Error('API failed'));
await expect(processUser(1)).rejects.toThrow('API failed');
});
});
from unittest.mock import Mock, patch, MagicMock
import pytest
def test_user_processor_with_mock(mocker):
# Using pytest-mock
mock_api = mocker.patch('module.fetch_user_data')
mock_api.return_value = {
'id': 1,
'name': 'John',
'email': '[email protected]'
}
result = process_user(1)
mock_api.assert_called_once_with(1)
assert result['name'] == 'JOHN'
def test_database_interaction():
# Mock database connection
mock_db = Mock()
mock_db.query.return_value = [{'id': 1, 'name': 'Test'}]
service = UserService(db=mock_db)
users = service.get_all_users()
mock_db.query.assert_called_once()
assert len(users) == 1
import pytest
from database import Database
@pytest.fixture(scope='session')
def database():
"""Session-wide database connection."""
db = Database(':memory:')
db.create_tables()
yield db
db.close()
@pytest.fixture
def sample_users():
"""Provide sample user data."""
return [
{'email': '[email protected]', 'name': 'User 1'},
{'email': '[email protected]', 'name': 'User 2'},
]
@pytest.fixture
def user_service(database):
"""Provide UserService with test database."""
return UserService(database)
def test_bulk_create(user_service, sample_users):
created = user_service.bulk_create(sample_users)
assert len(created) == 2
describe('DatabaseTests', () => {
let db;
beforeAll(async () => {
// Run once before all tests
db = await Database.connect(':memory:');
await db.createTables();
});
afterAll(async () => {
// Run once after all tests
await db.close();
});
beforeEach(async () => {
// Run before each test
await db.clearTables();
});
it('inserts user', async () => {
await db.insert('users', { name: 'Test' });
const users = await db.query('users');
expect(users).toHaveLength(1);
});
});
import pytest
from testcontainers.postgres import PostgresContainer
@pytest.fixture(scope='module')
def postgres_container():
"""Provide PostgreSQL container for integration tests."""
with PostgresContainer('postgres:15') as postgres:
yield postgres
@pytest.fixture
def db_connection(postgres_container):
"""Provide database connection."""
conn = create_connection(postgres_container.get_connection_url())
setup_schema(conn)
yield conn
conn.close()
def test_user_repository_integration(db_connection):
repo = UserRepository(db_connection)
# Insert user
user = repo.create({'email': '[email protected]', 'name': 'Test'})
assert user.id is not None
# Retrieve user
retrieved = repo.get_by_id(user.id)
assert retrieved.email == '[email protected]'
# Update user
repo.update(user.id, {'name': 'Updated'})
updated = repo.get_by_id(user.id)
assert updated.name == 'Updated'
import request from 'supertest';
import app from './app';
describe('API Integration Tests', () => {
it('creates and retrieves user', async () => {
// Create user
const createResponse = await request(app)
.post('/api/users')
.send({ email: '[email protected]', name: 'Test' })
.expect(201);
const userId = createResponse.body.id;
// Retrieve user
const getResponse = await request(app)
.get(`/api/users/${userId}`)
.expect(200);
expect(getResponse.body.email).toBe('[email protected]');
});
it('returns 404 for non-existent user', async () => {
await request(app)
.get('/api/users/999')
.expect(404);
});
});
import { test, expect } from '@playwright/test';
test.describe('User Registration', () => {
test('completes registration flow', async ({ page }) => {
// Navigate to registration page
await page.goto('/register');
// Fill form
await page.fill('[data-testid="email-input"]', '[email protected]');
await page.fill('[data-testid="password-input"]', 'SecurePass123');
await page.fill('[data-testid="name-input"]', 'Test User');
// Submit form
await page.click('[data-testid="submit-button"]');
// Wait for redirect
await page.waitForURL('/dashboard');
// Verify success
await expect(page.locator('[data-testid="welcome-message"]'))
.toContainText('Welcome, Test User');
});
test('shows validation errors', async ({ page }) => {
await page.goto('/register');
// Submit without filling
await page.click('[data-testid="submit-button"]');
// Check error messages
await expect(page.locator('[data-testid="email-error"]'))
.toContainText('Email is required');
});
});
describe('User Dashboard', () => {
beforeEach(() => {
// Login before each test
cy.login('[email protected]', 'password');
cy.visit('/dashboard');
});
it('displays user information', () => {
cy.get('[data-cy=user-name]').should('contain', 'Test User');
cy.get('[data-cy=user-email]').should('contain', '[email protected]');
});
it('updates profile', () => {
cy.get('[data-cy=edit-profile]').click();
cy.get('[data-cy=name-input]').clear().type('Updated Name');
cy.get('[data-cy=save-button]').click();
cy.get('[data-cy=success-message]')
.should('be.visible')
.and('contain', 'Profile updated');
});
});
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm run test:unit
- name: Run integration tests
run: npm run test:integration
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/test
- name: Run E2E tests
run: npm run test:e2e
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
# Run with coverage
npm test -- --coverage
# Coverage thresholds in package.json
{
"jest": {
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
}
}
}
}
# Run with coverage
pytest --cov=src --cov-report=html --cov-report=term
# Config in .coveragerc
[run]
source = src
omit = */tests/*
[report]
fail_under = 80
tools
Lightweight orchestrator for spec-before-plan workflow. Use when starting a feature with ambiguous requirements. Walks SPEC.md → PLAN.md → execute, delegating to /superpowers:writing-plans and /superpowers:executing-plans. Invoke when asked to "spec this out", "spec-first", "spec and plan for X", or when feature requirements are vague.
tools
Problem Statement Co-Authoring Skill
development
Structure and maintain professional brag documents with clear templates for accomplishments, projects, and growth tracking. Use when documenting achievements, creating brag document entries, formatting accomplishments, or tracking career progress.
development
Analyze technical documentation for clarity, conciseness, and effectiveness using Google Technical Writing principles. Use when reviewing documentation, checking writing quality, improving docs, or providing writing feedback.