Skip to main content
Glama

run_tests

Execute tests for the active Xcode project to verify code functionality and identify issues. Optionally specify a test plan to run specific test suites.

Instructions

Executes tests for the active Xcode project.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
testPlanNoOptional name of the test plan to run.

Implementation Reference

  • Main handler implementing the run_tests tool logic: runs xcodebuild tests with filtering options, captures and processes output, generates logs, test reports, and code coverage.
    private async runTests(
        projectPath: string,
        scheme: string,
        configuration: string = "Debug",
        testIdentifier?: string,
        skipTests?: string[],
        destination: string = "platform=iOS Simulator,name=iPhone 15 Pro"
    ): Promise<{ success: boolean; output: string; logPath: string }> {
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        const logPath = path.join(this.buildLogsDir, `test-${timestamp}.log`);
        const projectDir = path.dirname(projectPath);
        const reportsPath = path.join(projectDir, 'TestReports', `Reports-${timestamp}`);
        const xcresultPath = `${reportsPath}.xcresult`;
    
        try {
            await mkdir(path.join(projectDir, 'TestReports'), { recursive: true });
        } catch (error) {
            console.error(`Failed to prepare test reports directory: ${error}`);
        }
    
        let testFlags = 'test';
        if (testIdentifier) {
            testFlags += ` -only-testing:${testIdentifier}`;
        }
        if (skipTests?.length) {
            testFlags += ` ${skipTests.map(test => `-skip-testing:${test}`).join(' ')}`;
        }
    
        const command = `which xcodebuild && xcodebuild -project "${projectPath}" \
            -scheme "${scheme}" \
            -configuration "${configuration}" \
            -destination '${destination}' \
            -resultBundlePath "${xcresultPath}" \
            -enableCodeCoverage YES \
            -UseModernBuildSystem=YES \
            -json \
            clean ${testFlags} 2>&1 | tee ${logPath}`;
    
        try {
            const { stdout, stderr } = await execAsync(command, { maxBuffer: 100 * 1024 * 1024 });
    
            try {
                const jsonOutput = stdout.split('\n')
                    .filter(line => line.trim())
                    .map(line => {
                        try { return JSON.parse(line); }
                        catch (e) { return line; }
                    });
                await writeFile(logPath + '.json', JSON.stringify(jsonOutput, null, 2));
            } catch (parseError) {
                console.error('Failed to parse JSON output:', parseError);
            }
    
            // Process test results using xcresulttool
            if (fs.existsSync(xcresultPath)) {
                try {
                    // Get test summary
                    const summaryCmd = `xcrun xcresulttool get --format json --path "${xcresultPath}"`;
                    const { stdout: summaryOutput } = await execAsync(summaryCmd);
                    await writeFile(path.join(this.buildLogsDir, `test-summary-${timestamp}.json`), summaryOutput);
    
                    // Get code coverage if available
                    const coverageOutput = await execAsync(`xcrun xccov view --report "${xcresultPath}"`);
                    await writeFile(path.join(this.buildLogsDir, `coverage-${timestamp}.txt`), coverageOutput.stdout);
                } catch (resultsError) {
                    console.error('Failed to process test results:', resultsError);
                }
            }
    
            const success = !stdout.includes('** TEST FAILED **') && !stdout.includes('** BUILD FAILED **');
            return { success, output: stdout + stderr, logPath };
        } catch (error) {
            console.error('Test error:', error);
            if (error instanceof Error) {
                const execError = error as { stderr?: string };
                const errorOutput = error.message + (execError.stderr ? `\n${execError.stderr}` : '');
                await writeFile(logPath, errorOutput);
                return { success: false, output: errorOutput, logPath };
            }
            throw error;
        }
    }
  • src/index.ts:295-325 (registration)
    Registers the "run_tests" tool in the MCP ListToolsRequestSchema handler, defining its name, description, and JSON input schema.
        name: "run_tests",
        description: "Run Xcode project tests with optional filtering",
        inputSchema: {
            type: "object",
            properties: {
                projectPath: {
                    type: "string",
                    description: "Path to the .xcodeproj or .xcworkspace"
                },
                scheme: {
                    type: "string",
                    description: "Test scheme name"
                },
                testIdentifier: {
                    type: "string",
                    description: "Optional specific test to run (e.g., 'MyTests/testExample')"
                },
                skipTests: {
                    type: "array",
                    items: { type: "string" },
                    description: "Optional array of test identifiers to skip"
                },
                configuration: {
                    type: "string",
                    description: "Build configuration (e.g., Debug, Release)",
                    default: "Debug"
                }
            },
            required: ["projectPath", "scheme"]
        }
    }]
  • TypeScript interface and type guard for validating run_tests tool input arguments.
    interface TestArguments {
        projectPath: string;
        scheme: string;
        testIdentifier?: string;
        skipTests?: string[];
        configuration?: string;
        destination?: string;
    }
    
    interface BuildOptions extends BuildArguments {
        includeWarnings?: boolean;
    }
    
    interface TestOptions extends TestArguments {
        includeWarnings?: boolean;
    }
    
    // Add these type guard functions
    function isBuildOptions(args: unknown): args is BuildOptions {
        if (!isBuildArguments(args)) return false;
        const a = args as Partial<BuildOptions>;
        return a.includeWarnings === undefined || typeof a.includeWarnings === 'boolean';
    }
    
    function isTestOptions(args: unknown): args is TestOptions {
        if (!isTestArguments(args)) return false;
        const a = args as Partial<TestOptions>;
        return a.includeWarnings === undefined || typeof a.includeWarnings === 'boolean';
    }
    
    function isTestArguments(args: unknown): args is TestArguments {
        if (typeof args !== 'object' || args === null) return false;
        const a = args as Partial<TestArguments>;
        return (
            typeof a.projectPath === 'string' &&
            typeof a.scheme === 'string' &&
            (a.testIdentifier === undefined || typeof a.testIdentifier === 'string') &&
            (a.skipTests === undefined || (Array.isArray(a.skipTests) && a.skipTests.every(t => typeof t === 'string'))) &&
            (a.configuration === undefined || typeof a.configuration === 'string') &&
            (a.destination === undefined || typeof a.destination === 'string')
        );
    }
  • MCP CallToolRequestSchema dispatch case for "run_tests": validates arguments and invokes the runTests handler.
    case "run_tests": {
        if (!isTestArguments(request.params.arguments)) {
            throw new McpError(ErrorCode.InvalidParams, "Invalid test arguments provided");
        }
        const result = await this.runTests(
            request.params.arguments.projectPath,
            request.params.arguments.scheme,
            request.params.arguments.configuration,
            request.params.arguments.testIdentifier,
            request.params.arguments.skipTests,
            request.params.arguments.destination
        );
        this.latestBuildLog = result.logPath;
        return {
            content: [{
                type: "text",
                text: result.output
            }],
            isError: !result.success
        };
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/PolarVista/Xcode-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server