| type | skill |
| name | Test Generation |
| description | Generate comprehensive test cases for code |
| skillSlug | test-generation |
| phases | ["E","V"] |
| generated | "2026-03-18T00:00:00.000Z" |
| status | filled |
| scaffoldVersion | 2.0.0 |
Test Generation
Generate and maintain tests for the @dotcontext/cli project using Jest with ts-jest.
When to Use
- Writing tests for new services, generators, or utilities
- Adding test coverage for MCP gateway handlers
- Creating integration tests for CLI commands
- Validating bug fixes with regression tests
- During Execution (E) and Validation (V) phases
Test Configuration
- Framework: Jest with ts-jest preset
- Config:
jest.config.js at project root
- Test location: Co-located with source files (
*.test.ts) or in __tests__/ directories
- Test match patterns:
**/__tests__/**/*.ts, **/?(*.)+(spec|test).ts
- Root:
src/
npm test
npx jest src/services/mcp/mcpServer.test.ts
npx jest --testPathPattern="mcp"
npx jest --coverage
Test Patterns by Layer
Service Tests
Services are the most common test target. Follow the pattern in current service and MCP tests:
import * as os from 'os';
import * as path from 'path';
import * as fs from 'fs-extra';
import { MyService } from './myService';
import type { CLIInterface } from '../../utils/cliUI';
import type { TranslateFn } from '../../utils/i18n';
let tempDir: string;
beforeEach(async () => {
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'test-'));
});
afterEach(async () => {
await fs.remove(tempDir);
});
function createMockUI(): CLIInterface {
return {
displayWelcome: jest.fn(),
displayProjectInfo: jest.fn(),
displayStep: jest.fn(),
displaySuccess: jest.fn(),
displayWarning: jest.fn(),
displayError: jest.fn(),
};
}
const mockT: TranslateFn = ((key: string) => key) as TranslateFn;
Key patterns:
- Temp directories: Always use
fs.mkdtemp() and clean up in afterEach
- Mock CLIInterface: Provide a mock with
jest.fn() for each method
- Mock TranslateFn: Simple passthrough
(key) => key
- Mock tool or gateway boundaries: Use
jest.mock() at module level for filesystem, prompt loaders, or MCP helpers
MCP Gateway Handler Tests
Test handlers directly, not through MCP transport:
import { handleExplore, ExploreParams } from './gateway/explore';
describe('handleExplore', () => {
let tempDir: string;
beforeEach(async () => {
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'explore-'));
await fs.writeFile(path.join(tempDir, 'test.ts'), 'export const x = 1;');
});
afterEach(async () => {
await fs.remove(tempDir);
});
it('should read a file', async () => {
const result = await handleExplore(
{ action: 'read', filePath: path.join(tempDir, 'test.ts') },
{ repoPath: tempDir }
);
const payload = JSON.parse(result.content[0].text);
expect(payload.success).toBe(true);
expect(payload.content).toContain('export const x');
});
it('should return error for missing file', async () => {
const result = await handleExplore(
{ action: 'read', filePath: path.join(tempDir, 'missing.ts') },
{ repoPath: tempDir }
);
expect(result.isError).toBe(true);
});
});
Generator Tests
Test that generators produce correct file structures:
import { SkillGenerator } from './skillGenerator';
describe('SkillGenerator', () => {
let tempDir: string;
beforeEach(async () => {
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'gen-'));
});
it('should generate skill directories', async () => {
const generator = new SkillGenerator({ repoPath: tempDir });
const result = await generator.generate({ skills: ['commit-message'] });
expect(result.generatedSkills).toContain('commit-message');
expect(fs.existsSync(path.join(tempDir, '.context/skills/commit-message/SKILL.md'))).toBe(true);
});
it('should skip existing skills when force is false', async () => {
const generator = new SkillGenerator({ repoPath: tempDir });
await generator.generate({ skills: ['commit-message'] });
const result = await generator.generate({ skills: ['commit-message'], force: false });
expect(result.skippedSkills).toContain('commit-message');
});
});
Utility Tests
Test pure functions directly:
import { parseFrontMatter, isScaffoldFrontmatter } from './frontMatter';
describe('frontMatter', () => {
it('should parse v2 scaffold frontmatter', () => {
const content = `---
type: skill
name: Test
status: filled
scaffoldVersion: "2.0.0"
---
# Content`;
const result = parseFrontMatter(content);
expect(isScaffoldFrontmatter(result)).toBe(true);
expect(result.status).toBe('filled');
});
});
Workflow Tests
Test phase transitions and gate logic:
import { GateChecker } from './gateChecker';
describe('GateChecker', () => {
it('should block P->R transition when plan required but missing', () => {
const checker = new GateChecker({ require_plan: true });
const result = checker.canAdvance('P', 'R', { hasPlan: false });
expect(result.allowed).toBe(false);
expect(result.reason).toContain('plan');
});
});
What to Mock
| Dependency | Mock Strategy | Example |
|---|
| MCP tools / gateway helpers | jest.mock() at module level | MCP service tests |
| File system (for isolation) | Use fs.mkdtemp() temp dirs | All service tests |
| Prompt loader | jest.mock('../../utils/promptLoader') | Return test prompt |
| CLI interface | Manual mock object | createMockUI() helper |
| Translate function | Passthrough (key) => key | All CLI-facing tests |
| tree-sitter (optional dep) | Test with and without | Semantic analysis tests |
Test Checklist
Integration Tests
For end-to-end testing of current interfaces, see src/cli.test.ts and src/services/mcp/mcpServer.test.ts:
Integration tests are slower and may require:
- Real
.context/ directory structure
- Actual file writes (in temp dirs)
- Mocked AI responses (but real service orchestration)
Run integration tests separately if needed:
npx jest --testPathPattern="integration"