From architect
Test naming conventions, fixture patterns, mocking strategies, and coverage thresholds by stage
npx claudepluginhub navraj007in/architecture-cowork-plugin --plugin architectThis skill uses the workspace's default tool permissions.
Covers unit, integration, and e2e test patterns for all supported frameworks.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
Covers unit, integration, and e2e test patterns for all supported frameworks.
src/services/user.ts → src/services/__tests__/user.test.ts (Jest/Vitest)src/services/user.ts → src/services/test_user.py (pytest)tests/ directory at project root
tests/integration/auth.test.tstests/integration/database.test.tse2e/ or tests/e2e/ directory
e2e/flows/login.test.tse2e/flows/checkout.test.ts<module>.test.ts or <module>_test.py<feature>.integration.test.ts or test_<feature>.py<flow>.e2e.test.ts or test_<flow>_e2e.pyAll tests MUST follow AAA pattern:
describe('UserService', () => {
describe('createUser', () => {
it('should create a user with valid email and name', () => {
// ARRANGE: Setup test data, mocks, fixtures
const email = 'alice@example.com';
const name = 'Alice Chen';
// ACT: Call the function under test
const user = service.createUser(email, name);
// ASSERT: Verify the result
expect(user.email).toBe(email);
expect(user.name).toBe(name);
expect(user.id).toBeDefined();
});
});
});
Golden rule: One logical assertion per test (one reason to fail).
❌ BAD:
it('works', () => { ... });
it('user creation 1', () => { ... });
✅ GOOD:
it('should create a user with valid email and return user object with id', () => { ... });
it('should reject email without @ symbol', () => { ... });
it('should hash password before storing in database', () => { ... });
Pattern: should [what happens] [given conditions if not obvious]
// jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src', '<rootDir>/tests'],
testMatch: ['**/__tests__/**/*.ts', '**/*.test.ts'],
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/index.ts'
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}
};
// tsconfig.test.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"jsx": "react",
"types": ["jest", "node"]
}
}
# pytest.ini
[pytest]
testpaths = tests
python_files = test_*.py *_test.py
python_classes = Test*
python_functions = test_*
addopts = --verbose --strict-markers
markers =
unit: unit tests
integration: integration tests
slow: slow running tests
# conftest.py — shared fixtures
import pytest
@pytest.fixture
def sample_user():
return {'id': 1, 'email': 'test@example.com', 'name': 'Test User'}
// *_test.go convention
package user
import "testing"
func TestCreateUser(t *testing.T) {
user := CreateUser("alice@example.com", "Alice")
if user.Email != "alice@example.com" {
t.Errorf("expected %q, got %q", "alice@example.com", user.Email)
}
}
public class UserServiceTests
{
[Fact]
public void CreateUser_WithValidEmail_ReturnsUserWithId()
{
// Arrange
var service = new UserService();
// Act
var user = service.CreateUser("alice@example.com", "Alice");
// Assert
Assert.NotNull(user.Id);
Assert.Equal("alice@example.com", user.Email);
}
}
Use in-memory database for unit tests when possible:
// jest.config.js for Node backend
module.exports = {
setupFilesAfterEnv: ['<rootDir>/tests/setup.ts']
};
// tests/setup.ts
import { PrismaClient } from '@prisma/client';
beforeAll(async () => {
// Point to test database
process.env.DATABASE_URL = 'file:./test.db';
await prisma.$executeRawUnsafe('PRAGMA foreign_keys = OFF');
});
afterEach(async () => {
// Clean up between tests
await prisma.$queryRaw`DELETE FROM "User"`;
await prisma.$queryRaw`DELETE FROM "Post"`;
});
afterAll(async () => {
await prisma.$disconnect();
});
Mock HTTP calls, external APIs, payment gateways:
// jest.mock for modules
jest.mock('../lib/stripe', () => ({
createPayment: jest.fn().mockResolvedValue({ id: 'pay_123', status: 'succeeded' })
}));
// Manual mock for fetch
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ data: [] })
})
);
Use actual test database for integration tests:
// tests/integration/setup.ts
beforeAll(async () => {
// Spin up test PostgreSQL via docker-compose.test.yml
process.env.DATABASE_URL = 'postgresql://test:test@localhost:5433/test_db';
await prisma.$executeRawUnsafe('CREATE SCHEMA IF NOT EXISTS tests');
});
afterAll(async () => {
await prisma.$executeRawUnsafe('DROP SCHEMA tests CASCADE');
});
Rule: If a test needs the database to run, it's an integration test, not a unit test. Don't mock the database for integration tests.
// tests/fixtures/user.fixtures.ts
export const validUser = {
email: 'alice@example.com',
name: 'Alice Chen',
password: 'SecurePass123!'
};
export const adminUser = {
...validUser,
role: 'admin'
};
export const inactiveUser = {
...validUser,
status: 'inactive'
};
// In test file
import { validUser, adminUser } from './fixtures/user.fixtures';
it('should allow admin to create users', () => {
const created = service.createUser(validUser, { createdBy: adminUser });
expect(created.createdBy).toBe(adminUser.id);
});
// tests/factories/user.factory.ts
class UserFactory {
static async create(overrides = {}) {
const defaults = {
email: `user-${Date.now()}@example.com`,
name: 'Test User',
role: 'user'
};
return prisma.user.create({
data: { ...defaults, ...overrides }
});
}
static async createMany(count, overrides = {}) {
return Promise.all(
Array.from({ length: count }).map(() => this.create(overrides))
);
}
}
// In test
const users = await UserFactory.createMany(10, { role: 'admin' });
expect(users).toHaveLength(10);
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test:unit": "jest --testPathPattern=__tests__",
"test:integration": "jest --testPathPattern=integration",
"test:e2e": "playwright test"
}
}
pytest # all tests
pytest -m unit # unit tests only
pytest -m integration # integration tests
pytest -v --cov=src # with coverage
go test ./... # all tests
go test -cover ./... # with coverage
go test -run TestCreateUser # specific test
// Always return promise or use async/await
it('should fetch user by id', async () => {
const user = await service.getUserById(1);
expect(user.id).toBe(1);
});
// Expect promises to resolve/reject
it('should reject invalid id', async () => {
await expect(service.getUserById(-1)).rejects.toThrow('Invalid ID');
});
import pytest
@pytest.mark.asyncio
async def test_fetch_user():
user = await service.get_user(1)
assert user.id == 1
# Or use sync wrapper
from asyncio import run
def test_sync_wrapper():
user = run(service.get_user(1))
assert user.id == 1
// Jest
it.skip('should handle network timeout', () => {
// Re-enable after fixing flakiness
});
// pytest
@pytest.mark.skip(reason="flaky, TODO: fix race condition")
def test_concurrent_updates():
pass
// jest.config.js
module.exports = {
testTimeout: 30000 // 30s for slow tests
};
// In test
jest.setTimeout(5000); // override for this test
it.todo('should support bulk user import');
- name: Run Tests
run: npm test -- --coverage
- name: Upload Coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
- name: Check Coverage
run: |
COVERAGE=$(npm test -- --coverage | grep Lines | awk '{print $NF}' | sed 's/%//')
if (( $(echo "$COVERAGE < 80" | bc -l) )); then
echo "Coverage $COVERAGE% below threshold 80%"
exit 1
fi