seed-skills/mcp-server-testing/SKILL.md
Comprehensive testing patterns for Model Context Protocol servers including tool validation, transport testing, schema verification, and end-to-end MCP integration testing with stdio and SSE transports.
npx skillsauth add PramodDutta/qaskills MCP Server TestingInstall 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.
You are an expert in testing Model Context Protocol (MCP) servers. When the user asks you to write tests for MCP servers, validate MCP tools, test transport layers, or verify MCP integrations, follow these detailed instructions to produce comprehensive, production-ready test suites.
tests/
mcp/
unit/
tools/
tool-schema.test.ts
tool-execution.test.ts
tool-error-handling.test.ts
resources/
resource-read.test.ts
resource-subscribe.test.ts
resource-templates.test.ts
prompts/
prompt-list.test.ts
prompt-get.test.ts
prompt-arguments.test.ts
integration/
transport/
stdio-transport.test.ts
sse-transport.test.ts
streamable-http.test.ts
session/
initialization.test.ts
capability-negotiation.test.ts
multi-turn.test.ts
lifecycle/
server-startup.test.ts
graceful-shutdown.test.ts
reconnection.test.ts
e2e/
full-flow.test.ts
concurrent-clients.test.ts
error-recovery.test.ts
fixtures/
mock-tools.ts
mock-resources.ts
sample-requests.ts
sample-responses.ts
helpers/
mcp-test-client.ts
transport-factory.ts
assertion-helpers.ts
config/
vitest.mcp.config.ts
// tests/helpers/mcp-test-client.ts
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
import { spawn, ChildProcess } from 'child_process';
interface MCPTestClientOptions {
transport: 'stdio' | 'sse';
serverCommand?: string;
serverArgs?: string[];
serverUrl?: string;
timeout?: number;
}
export class MCPTestClient {
private client: Client;
private serverProcess: ChildProcess | null = null;
private transport: StdioClientTransport | SSEClientTransport;
constructor(private options: MCPTestClientOptions) {
this.client = new Client(
{ name: 'mcp-test-client', version: '1.0.0' },
{ capabilities: {} }
);
}
async connect(): Promise<void> {
if (this.options.transport === 'stdio') {
const command = this.options.serverCommand || 'node';
const args = this.options.serverArgs || ['dist/index.js'];
this.transport = new StdioClientTransport({
command,
args,
env: { ...process.env, NODE_ENV: 'test' },
});
} else {
const url = this.options.serverUrl || 'http://localhost:3001/sse';
this.transport = new SSEClientTransport(new URL(url));
}
await this.client.connect(this.transport);
}
async listTools(): Promise<any> {
return this.client.request({ method: 'tools/list' }, {} as any);
}
async callTool(name: string, args: Record<string, unknown>): Promise<any> {
return this.client.request(
{
method: 'tools/call',
params: { name, arguments: args },
},
{} as any
);
}
async listResources(): Promise<any> {
return this.client.request({ method: 'resources/list' }, {} as any);
}
async readResource(uri: string): Promise<any> {
return this.client.request(
{
method: 'resources/read',
params: { uri },
},
{} as any
);
}
async listPrompts(): Promise<any> {
return this.client.request({ method: 'prompts/list' }, {} as any);
}
async getPrompt(name: string, args?: Record<string, string>): Promise<any> {
return this.client.request(
{
method: 'prompts/get',
params: { name, arguments: args },
},
{} as any
);
}
async disconnect(): Promise<void> {
await this.client.close();
if (this.serverProcess) {
this.serverProcess.kill('SIGTERM');
this.serverProcess = null;
}
}
}
export function createTestClient(
options: Partial<MCPTestClientOptions> = {}
): MCPTestClient {
return new MCPTestClient({
transport: 'stdio',
serverCommand: 'npx',
serverArgs: ['tsx', 'src/index.ts'],
timeout: 10000,
...options,
});
}
// tests/unit/tools/tool-schema.test.ts
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import Ajv from 'ajv';
import { createTestClient, MCPTestClient } from '../../helpers/mcp-test-client';
describe('MCP Tool Schema Validation', () => {
let client: MCPTestClient;
const ajv = new Ajv({ strict: false, allErrors: true });
beforeAll(async () => {
client = createTestClient();
await client.connect();
});
afterAll(async () => {
await client.disconnect();
});
it('should list all available tools with valid schemas', async () => {
const result = await client.listTools();
expect(result.tools).toBeDefined();
expect(Array.isArray(result.tools)).toBe(true);
expect(result.tools.length).toBeGreaterThan(0);
for (const tool of result.tools) {
expect(tool.name).toBeDefined();
expect(typeof tool.name).toBe('string');
expect(tool.name.length).toBeGreaterThan(0);
expect(tool.description).toBeDefined();
expect(typeof tool.description).toBe('string');
if (tool.inputSchema) {
expect(tool.inputSchema.type).toBe('object');
const isValid = ajv.validateSchema(tool.inputSchema);
expect(isValid).toBe(true);
}
}
});
it('should have unique tool names', async () => {
const result = await client.listTools();
const names = result.tools.map((t: any) => t.name);
const uniqueNames = new Set(names);
expect(uniqueNames.size).toBe(names.length);
});
it('should enforce required properties in input schemas', async () => {
const result = await client.listTools();
for (const tool of result.tools) {
if (tool.inputSchema?.required) {
expect(Array.isArray(tool.inputSchema.required)).toBe(true);
for (const requiredProp of tool.inputSchema.required) {
expect(tool.inputSchema.properties).toHaveProperty(requiredProp);
}
}
}
});
it('should validate tool input schema property types', async () => {
const result = await client.listTools();
const validTypes = ['string', 'number', 'integer', 'boolean', 'array', 'object', 'null'];
for (const tool of result.tools) {
if (tool.inputSchema?.properties) {
for (const [propName, propSchema] of Object.entries(tool.inputSchema.properties)) {
const schema = propSchema as any;
if (schema.type) {
const types = Array.isArray(schema.type) ? schema.type : [schema.type];
for (const type of types) {
expect(validTypes).toContain(type);
}
}
}
}
}
});
});
// tests/unit/tools/tool-execution.test.ts
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { createTestClient, MCPTestClient } from '../../helpers/mcp-test-client';
describe('MCP Tool Execution', () => {
let client: MCPTestClient;
beforeAll(async () => {
client = createTestClient();
await client.connect();
});
afterAll(async () => {
await client.disconnect();
});
it('should execute a tool with valid arguments', async () => {
const tools = await client.listTools();
const firstTool = tools.tools[0];
const minimalArgs: Record<string, unknown> = {};
if (firstTool.inputSchema?.required) {
for (const prop of firstTool.inputSchema.required) {
const propSchema = firstTool.inputSchema.properties[prop];
minimalArgs[prop] = generateMinimalValue(propSchema);
}
}
const result = await client.callTool(firstTool.name, minimalArgs);
expect(result).toBeDefined();
expect(result.content).toBeDefined();
expect(Array.isArray(result.content)).toBe(true);
for (const item of result.content) {
expect(['text', 'image', 'resource']).toContain(item.type);
}
});
it('should return isError flag on tool execution failure', async () => {
const result = await client.callTool('nonexistent-tool', {});
expect(result.isError).toBe(true);
expect(result.content).toBeDefined();
expect(result.content[0].type).toBe('text');
});
it('should handle missing required arguments gracefully', async () => {
const tools = await client.listTools();
const toolWithRequired = tools.tools.find(
(t: any) => t.inputSchema?.required?.length > 0
);
if (toolWithRequired) {
const result = await client.callTool(toolWithRequired.name, {});
expect(result.isError).toBe(true);
}
});
it('should handle null and undefined arguments', async () => {
const tools = await client.listTools();
const firstTool = tools.tools[0];
const result = await client.callTool(firstTool.name, {
unexpectedParam: null,
anotherParam: undefined,
} as any);
expect(result).toBeDefined();
});
it('should respect tool execution timeouts', async () => {
const startTime = Date.now();
const TIMEOUT_MS = 30000;
try {
await Promise.race([
client.callTool('long-running-tool', {}),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), TIMEOUT_MS)
),
]);
} catch (error: any) {
const elapsed = Date.now() - startTime;
expect(elapsed).toBeLessThan(TIMEOUT_MS + 1000);
}
});
});
function generateMinimalValue(schema: any): unknown {
switch (schema?.type) {
case 'string':
return schema.enum ? schema.enum[0] : 'test-value';
case 'number':
case 'integer':
return schema.minimum ?? 0;
case 'boolean':
return false;
case 'array':
return [];
case 'object':
return {};
default:
return 'test';
}
}
// tests/integration/transport/stdio-transport.test.ts
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { spawn, ChildProcess } from 'child_process';
describe('MCP Stdio Transport', () => {
let serverProcess: ChildProcess;
beforeEach(() => {
serverProcess = spawn('npx', ['tsx', 'src/index.ts'], {
stdio: ['pipe', 'pipe', 'pipe'],
env: { ...process.env, NODE_ENV: 'test' },
});
});
afterEach(() => {
if (serverProcess) {
serverProcess.kill('SIGTERM');
}
});
it('should respond to initialize request via stdio', async () => {
const initRequest = JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'initialize',
params: {
protocolVersion: '2024-11-05',
capabilities: {},
clientInfo: { name: 'test-client', version: '1.0.0' },
},
});
const response = await sendAndReceive(serverProcess, initRequest);
const parsed = JSON.parse(response);
expect(parsed.jsonrpc).toBe('2.0');
expect(parsed.id).toBe(1);
expect(parsed.result).toBeDefined();
expect(parsed.result.protocolVersion).toBeDefined();
expect(parsed.result.serverInfo).toBeDefined();
expect(parsed.result.capabilities).toBeDefined();
});
it('should handle malformed JSON gracefully', async () => {
const response = await sendAndReceive(serverProcess, 'not-valid-json');
const parsed = JSON.parse(response);
expect(parsed.error).toBeDefined();
expect(parsed.error.code).toBe(-32700); // Parse error
});
it('should handle invalid JSON-RPC method', async () => {
const request = JSON.stringify({
jsonrpc: '2.0',
id: 2,
method: 'nonexistent/method',
params: {},
});
const response = await sendAndReceive(serverProcess, request);
const parsed = JSON.parse(response);
expect(parsed.error).toBeDefined();
expect(parsed.error.code).toBe(-32601); // Method not found
});
it('should handle concurrent requests over stdio', async () => {
const requests = Array.from({ length: 5 }, (_, i) =>
JSON.stringify({
jsonrpc: '2.0',
id: i + 1,
method: 'tools/list',
params: {},
})
);
for (const req of requests) {
serverProcess.stdin!.write(req + '\n');
}
const responses: any[] = [];
await new Promise<void>((resolve) => {
let buffer = '';
serverProcess.stdout!.on('data', (data) => {
buffer += data.toString();
const lines = buffer.split('\n').filter(Boolean);
for (const line of lines) {
try {
responses.push(JSON.parse(line));
} catch {}
}
if (responses.length >= 5) resolve();
});
setTimeout(resolve, 5000);
});
expect(responses.length).toBeGreaterThanOrEqual(5);
const ids = responses.map((r) => r.id).sort();
expect(ids).toEqual([1, 2, 3, 4, 5]);
});
it('should handle server shutdown gracefully on SIGTERM', async () => {
const exitPromise = new Promise<number | null>((resolve) => {
serverProcess.on('exit', (code) => resolve(code));
});
serverProcess.kill('SIGTERM');
const exitCode = await exitPromise;
expect(exitCode).toBeNull(); // Null means terminated by signal
});
});
function sendAndReceive(
process: ChildProcess,
message: string,
timeout = 5000
): Promise<string> {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => reject(new Error('Timeout')), timeout);
process.stdout!.once('data', (data) => {
clearTimeout(timer);
resolve(data.toString().trim());
});
process.stdin!.write(message + '\n');
});
}
// tests/integration/transport/sse-transport.test.ts
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { spawn, ChildProcess } from 'child_process';
describe('MCP SSE Transport', () => {
let serverProcess: ChildProcess;
const SERVER_URL = 'http://localhost:3001';
beforeAll(async () => {
serverProcess = spawn('npx', ['tsx', 'src/index.ts', '--transport', 'sse'], {
stdio: ['pipe', 'pipe', 'pipe'],
env: { ...process.env, NODE_ENV: 'test', PORT: '3001' },
});
await waitForServer(SERVER_URL, 10000);
});
afterAll(() => {
if (serverProcess) {
serverProcess.kill('SIGTERM');
}
});
it('should establish SSE connection at /sse endpoint', async () => {
const response = await fetch(`${SERVER_URL}/sse`, {
headers: { Accept: 'text/event-stream' },
});
expect(response.status).toBe(200);
expect(response.headers.get('content-type')).toContain('text/event-stream');
});
it('should accept POST messages at /message endpoint', async () => {
const initRequest = {
jsonrpc: '2.0',
id: 1,
method: 'initialize',
params: {
protocolVersion: '2024-11-05',
capabilities: {},
clientInfo: { name: 'test-sse-client', version: '1.0.0' },
},
};
const response = await fetch(`${SERVER_URL}/message`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(initRequest),
});
expect(response.ok).toBe(true);
});
it('should reject non-SSE connections to /sse', async () => {
const response = await fetch(`${SERVER_URL}/sse`, {
headers: { Accept: 'application/json' },
});
expect(response.status).not.toBe(200);
});
it('should handle multiple concurrent SSE clients', async () => {
const connections = await Promise.all(
Array.from({ length: 3 }, () =>
fetch(`${SERVER_URL}/sse`, {
headers: { Accept: 'text/event-stream' },
})
)
);
for (const conn of connections) {
expect(conn.status).toBe(200);
}
});
it('should send keep-alive events', async () => {
const controller = new AbortController();
const response = await fetch(`${SERVER_URL}/sse`, {
headers: { Accept: 'text/event-stream' },
signal: controller.signal,
});
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let receivedData = '';
const readPromise = new Promise<string>(async (resolve) => {
while (true) {
const { done, value } = await reader.read();
if (done) break;
receivedData += decoder.decode(value);
if (receivedData.includes('event:')) {
resolve(receivedData);
break;
}
}
});
const result = await Promise.race([
readPromise,
new Promise<string>((resolve) => setTimeout(() => resolve('timeout'), 15000)),
]);
controller.abort();
expect(result).not.toBe('timeout');
});
});
async function waitForServer(url: string, timeout: number): Promise<void> {
const start = Date.now();
while (Date.now() - start < timeout) {
try {
await fetch(url);
return;
} catch {
await new Promise((resolve) => setTimeout(resolve, 500));
}
}
throw new Error(`Server at ${url} did not start within ${timeout}ms`);
}
// tests/unit/resources/resource-read.test.ts
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { createTestClient, MCPTestClient } from '../../helpers/mcp-test-client';
describe('MCP Resource Operations', () => {
let client: MCPTestClient;
beforeAll(async () => {
client = createTestClient();
await client.connect();
});
afterAll(async () => {
await client.disconnect();
});
it('should list all available resources', async () => {
const result = await client.listResources();
expect(result.resources).toBeDefined();
expect(Array.isArray(result.resources)).toBe(true);
for (const resource of result.resources) {
expect(resource.uri).toBeDefined();
expect(typeof resource.uri).toBe('string');
expect(resource.name).toBeDefined();
}
});
it('should read a resource by URI', async () => {
const resources = await client.listResources();
if (resources.resources.length > 0) {
const firstResource = resources.resources[0];
const result = await client.readResource(firstResource.uri);
expect(result.contents).toBeDefined();
expect(Array.isArray(result.contents)).toBe(true);
expect(result.contents.length).toBeGreaterThan(0);
for (const content of result.contents) {
expect(content.uri).toBeDefined();
expect(content.text || content.blob).toBeDefined();
}
}
});
it('should return error for non-existent resource', async () => {
try {
await client.readResource('nonexistent://resource/path');
expect.fail('Should have thrown an error');
} catch (error: any) {
expect(error).toBeDefined();
}
});
it('should handle resource MIME types correctly', async () => {
const resources = await client.listResources();
for (const resource of resources.resources) {
if (resource.mimeType) {
expect(typeof resource.mimeType).toBe('string');
expect(resource.mimeType).toMatch(/^[\w-]+\/[\w-+.]+$/);
}
}
});
});
// tests/unit/prompts/prompt-get.test.ts
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { createTestClient, MCPTestClient } from '../../helpers/mcp-test-client';
describe('MCP Prompt Operations', () => {
let client: MCPTestClient;
beforeAll(async () => {
client = createTestClient();
await client.connect();
});
afterAll(async () => {
await client.disconnect();
});
it('should list all available prompts', async () => {
const result = await client.listPrompts();
expect(result.prompts).toBeDefined();
expect(Array.isArray(result.prompts)).toBe(true);
for (const prompt of result.prompts) {
expect(prompt.name).toBeDefined();
expect(typeof prompt.name).toBe('string');
}
});
it('should get a prompt with arguments', async () => {
const prompts = await client.listPrompts();
if (prompts.prompts.length > 0) {
const firstPrompt = prompts.prompts[0];
const args: Record<string, string> = {};
if (firstPrompt.arguments) {
for (const arg of firstPrompt.arguments) {
if (arg.required) {
args[arg.name] = 'test-value';
}
}
}
const result = await client.getPrompt(firstPrompt.name, args);
expect(result.messages).toBeDefined();
expect(Array.isArray(result.messages)).toBe(true);
for (const message of result.messages) {
expect(['user', 'assistant']).toContain(message.role);
expect(message.content).toBeDefined();
}
}
});
it('should return error for missing required prompt arguments', async () => {
const prompts = await client.listPrompts();
const promptWithArgs = prompts.prompts.find(
(p: any) => p.arguments?.some((a: any) => a.required)
);
if (promptWithArgs) {
try {
await client.getPrompt(promptWithArgs.name, {});
expect.fail('Should have thrown');
} catch (error: any) {
expect(error).toBeDefined();
}
}
});
});
// tests/integration/session/initialization.test.ts
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { spawn, ChildProcess } from 'child_process';
describe('MCP Session Initialization', () => {
let serverProcess: ChildProcess;
beforeEach(() => {
serverProcess = spawn('npx', ['tsx', 'src/index.ts'], {
stdio: ['pipe', 'pipe', 'pipe'],
env: { ...process.env, NODE_ENV: 'test' },
});
});
afterEach(() => {
serverProcess?.kill('SIGTERM');
});
it('should complete full initialization handshake', async () => {
const initRequest = {
jsonrpc: '2.0',
id: 1,
method: 'initialize',
params: {
protocolVersion: '2024-11-05',
capabilities: { roots: { listChanged: true } },
clientInfo: { name: 'test-client', version: '1.0.0' },
},
};
const response = await sendMessage(serverProcess, initRequest);
expect(response.result.protocolVersion).toBe('2024-11-05');
expect(response.result.serverInfo.name).toBeDefined();
expect(response.result.capabilities).toBeDefined();
// Send initialized notification
const initializedNotification = {
jsonrpc: '2.0',
method: 'notifications/initialized',
};
serverProcess.stdin!.write(JSON.stringify(initializedNotification) + '\n');
});
it('should negotiate capabilities correctly', async () => {
const initRequest = {
jsonrpc: '2.0',
id: 1,
method: 'initialize',
params: {
protocolVersion: '2024-11-05',
capabilities: {
roots: { listChanged: true },
sampling: {},
},
clientInfo: { name: 'test-client', version: '1.0.0' },
},
};
const response = await sendMessage(serverProcess, initRequest);
const capabilities = response.result.capabilities;
// Server should declare which features it supports
if (capabilities.tools) {
expect(typeof capabilities.tools).toBe('object');
}
if (capabilities.resources) {
expect(typeof capabilities.resources).toBe('object');
}
if (capabilities.prompts) {
expect(typeof capabilities.prompts).toBe('object');
}
});
it('should reject requests before initialization', async () => {
const toolsRequest = {
jsonrpc: '2.0',
id: 1,
method: 'tools/list',
params: {},
};
const response = await sendMessage(serverProcess, toolsRequest);
expect(response.error).toBeDefined();
});
it('should reject unsupported protocol versions', async () => {
const initRequest = {
jsonrpc: '2.0',
id: 1,
method: 'initialize',
params: {
protocolVersion: '1999-01-01',
capabilities: {},
clientInfo: { name: 'test-client', version: '1.0.0' },
},
};
const response = await sendMessage(serverProcess, initRequest);
// Server should either error or negotiate a supported version
if (response.error) {
expect(response.error.code).toBeDefined();
} else {
expect(response.result.protocolVersion).not.toBe('1999-01-01');
}
});
});
async function sendMessage(process: ChildProcess, message: any): Promise<any> {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('Timeout')), 5000);
process.stdout!.once('data', (data) => {
clearTimeout(timeout);
try {
resolve(JSON.parse(data.toString().trim()));
} catch (e) {
reject(e);
}
});
process.stdin!.write(JSON.stringify(message) + '\n');
});
}
// tests/e2e/full-flow.test.ts
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { createTestClient, MCPTestClient } from '../helpers/mcp-test-client';
describe('MCP Full End-to-End Flow', () => {
let client: MCPTestClient;
beforeAll(async () => {
client = createTestClient();
await client.connect();
});
afterAll(async () => {
await client.disconnect();
});
it('should complete a full tool discovery and execution flow', async () => {
// Step 1: List available tools
const tools = await client.listTools();
expect(tools.tools.length).toBeGreaterThan(0);
// Step 2: Pick a tool and validate its schema
const selectedTool = tools.tools[0];
expect(selectedTool.name).toBeDefined();
expect(selectedTool.inputSchema).toBeDefined();
// Step 3: Execute the tool with valid arguments
const args: Record<string, unknown> = {};
if (selectedTool.inputSchema?.required) {
for (const prop of selectedTool.inputSchema.required) {
const schema = selectedTool.inputSchema.properties[prop];
args[prop] = generateValue(schema);
}
}
const result = await client.callTool(selectedTool.name, args);
expect(result.content).toBeDefined();
expect(result.content.length).toBeGreaterThan(0);
});
it('should complete a full resource discovery and read flow', async () => {
const resources = await client.listResources();
if (resources.resources.length > 0) {
const resource = resources.resources[0];
const content = await client.readResource(resource.uri);
expect(content.contents).toBeDefined();
expect(content.contents.length).toBeGreaterThan(0);
}
});
it('should complete a full prompt discovery and execution flow', async () => {
const prompts = await client.listPrompts();
if (prompts.prompts.length > 0) {
const prompt = prompts.prompts[0];
const args: Record<string, string> = {};
if (prompt.arguments) {
for (const arg of prompt.arguments) {
args[arg.name] = 'test-value';
}
}
const result = await client.getPrompt(prompt.name, args);
expect(result.messages).toBeDefined();
expect(result.messages.length).toBeGreaterThan(0);
}
});
it('should handle rapid sequential tool calls', async () => {
const tools = await client.listTools();
const tool = tools.tools[0];
const results = [];
for (let i = 0; i < 10; i++) {
const result = await client.callTool(tool.name, {});
results.push(result);
}
expect(results.length).toBe(10);
for (const result of results) {
expect(result.content).toBeDefined();
}
});
it('should maintain session state across multiple operations', async () => {
// First operation
const tools1 = await client.listTools();
// Second operation
const resources = await client.listResources();
// Third operation - tools should still be the same
const tools2 = await client.listTools();
expect(tools1.tools.length).toBe(tools2.tools.length);
expect(tools1.tools.map((t: any) => t.name).sort()).toEqual(
tools2.tools.map((t: any) => t.name).sort()
);
});
});
function generateValue(schema: any): unknown {
switch (schema?.type) {
case 'string':
return schema.enum ? schema.enum[0] : 'test-value';
case 'number':
return 42;
case 'integer':
return 1;
case 'boolean':
return true;
case 'array':
return [];
case 'object':
return {};
default:
return 'test';
}
}
// tests/config/vitest.mcp.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
include: ['tests/mcp/**/*.test.ts'],
testTimeout: 30000,
hookTimeout: 15000,
pool: 'forks',
poolOptions: {
forks: {
singleFork: true,
},
},
setupFiles: ['tests/mcp/setup.ts'],
reporters: ['verbose'],
env: {
NODE_ENV: 'test',
},
},
});
development
Build WebdriverIO E2E suites — wdio.conf.ts setup, $ and $$ selectors, auto-wait and waitUntil, Mocha framework structure, page objects, parallel capabilities, and services for visual testing and Appium mobile.
testing
Test Vue 3 components with Vue Test Utils and Vitest — mount vs shallowMount, finding and triggering DOM, asserting props and emitted events, awaiting async updates, and mocking Pinia stores and Vue Router.
testing
Write fast unit and integration tests with Vitest — vitest.config.ts setup, vi.fn and vi.mock module mocking, fake timers, snapshots, V8 coverage with thresholds, workspaces for monorepos, and in-source testing.
development
Practice strict red-green-refactor test-driven development — write one failing test first, make it pass with the minimum code, then refactor under green, with worked cycles in Jest and pytest, AAA structure, and behavior-based test naming.