skills/test/SKILL.md
Write tests against stubs following TDD red phase. Tests should fail initially (0% pass expected). Use after stubs to define expected behaviour before implementation.
npx skillsauth add sofer/.agents testInstall 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.
Write tests against stub implementations. This is the "red" phase of TDD - tests should fail because stubs throw NotImplementedError.
Tests define the expected behaviour before implementation:
Expect from orchestrator:
Each behaviour from spec becomes one or more tests:
# From spec
behaviours:
- scenario: "Successful user creation"
given: ["No user exists with email '[email protected]'"]
when: ["POST /users with valid email and name"]
then:
- "Returns 201 with user object"
- "User is persisted to database"
- "UserCreated event is published"
# Maps to tests
tests:
- "createUser returns user with generated id"
- "createUser persists user to repository"
- "createUser publishes UserCreated event"
Mirror the source structure:
src/
services/
user.service.ts
tests/
services/
user.service.test.ts
Or use co-location if project prefers:
src/
services/
user.service.ts
user.service.test.ts
Test each component in isolation using mocks:
// tests/services/user.service.test.ts
import { UserService } from '../../src/services/user.service';
import { IUserRepository } from '../../src/interfaces/user-repository.interface';
import { User, CreateUserInput } from '../../src/types/user.types';
describe('UserService', () => {
let userService: UserService;
let mockRepository: jest.Mocked<IUserRepository>;
beforeEach(() => {
mockRepository = {
save: jest.fn(),
findById: jest.fn(),
findByEmail: jest.fn(),
};
userService = new UserService(mockRepository);
});
describe('createUser', () => {
const validInput: CreateUserInput = {
email: '[email protected]',
name: 'Test User',
};
it('should return user with generated id', async () => {
mockRepository.findByEmail.mockResolvedValue(null);
mockRepository.save.mockImplementation(async (user) => user);
const result = await userService.createUser(validInput);
expect(result.id).toBeDefined();
expect(result.email).toBe(validInput.email);
expect(result.name).toBe(validInput.name);
});
it('should persist user to repository', async () => {
mockRepository.findByEmail.mockResolvedValue(null);
mockRepository.save.mockImplementation(async (user) => user);
await userService.createUser(validInput);
expect(mockRepository.save).toHaveBeenCalledWith(
expect.objectContaining({
email: validInput.email,
name: validInput.name,
})
);
});
it('should throw DuplicateEmailError when email exists', async () => {
const existingUser: User = {
id: '123',
email: validInput.email,
name: 'Existing',
createdAt: new Date(),
};
mockRepository.findByEmail.mockResolvedValue(existingUser);
await expect(userService.createUser(validInput))
.rejects.toThrow('DuplicateEmailError');
});
it('should normalise email to lowercase', async () => {
const inputWithUppercase = { ...validInput, email: '[email protected]' };
mockRepository.findByEmail.mockResolvedValue(null);
mockRepository.save.mockImplementation(async (user) => user);
const result = await userService.createUser(inputWithUppercase);
expect(result.email).toBe('[email protected]');
});
});
});
Test component interactions:
// tests/integration/user-registration.test.ts
describe('User Registration Integration', () => {
it('should create user and publish event', async () => {
// Test the full flow with real (or test) implementations
});
});
From spec edge cases, create specific tests:
describe('edge cases', () => {
it('should handle email at maximum length', async () => {
const longEmail = 'a'.repeat(64) + '@example.com';
// ...
});
it('should reject email exceeding maximum length', async () => {
const tooLongEmail = 'a'.repeat(65) + '@example.com';
// ...
});
it('should handle concurrent creation attempts', async () => {
// ...
});
});
Execute tests to confirm they fail appropriately:
# Jest
npm test
# Pytest
pytest
# Go
go test ./...
Expected result: All tests fail with "Not implemented" errors.
it('should do something', () => {
// Arrange
const input = { ... };
mockDep.method.mockReturnValue(...);
// Act
const result = service.method(input);
// Assert
expect(result).toEqual(...);
});
describe('given a valid user input', () => {
describe('when createUser is called', () => {
it('then should return created user', () => {
// ...
});
});
});
test:
story_id: "US-001"
files_created:
- path: "tests/services/user.service.test.ts"
test_count: 8
coverage:
behaviours: ["Successful creation", "Duplicate rejection"]
edge_cases: ["Max length email", "Uppercase normalisation"]
- path: "tests/repositories/user.repository.test.ts"
test_count: 4
run_result:
command: "npm test"
total: 12
passed: 0
failed: 12
status: "red" # Expected
behaviours_covered:
- "Successful user creation"
- "Duplicate email rejection"
- "Email normalisation"
edge_cases_covered:
- "Email at boundary length"
- "Concurrent creation"
notes: "All tests fail as expected - stubs not implemented"
Update manifest:
stories:
US-001:
phase: "test"
artifacts:
tests: "tests/services/user.service.test.ts"
Must pass before proceeding to implement phase:
tools
Check whether Claude and Codex have equivalent access to shared agent resources, skills, hooks, plugins, MCP servers, permissions, startup behaviour, and provider-specific adapter config. Use when comparing agent environments, debugging missing capabilities after restart, or deciding whether to symlink a resource or configure a runtime.
testing
Record substantive skill use in an append-only local log. Use after choosing or invoking a non-system skill for real work, when a skill is inspected but not used, or when a skill fails to apply. Do not use for routine system skills or incidental file reads.
testing
Turn a vague or underspecified request into a self-contained problem statement. Use when the user has a rough idea, when a request would fail if handed directly to an agent, or before non-trivial work that needs shared understanding.
data-ai
Append a one-line learning to ~/.agents/learning-log.md. Use when the user types /learning, or when something genuinely worth remembering surfaced during work and the user confirms it should be captured.