mcp-csharp-test

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

C# MCP Server Testing

C# MCP 服务器测试

Test MCP servers at two levels: unit tests for individual tool methods, and integration tests that exercise the full MCP protocol in-memory.
从两个层级测试MCP服务器:针对单个工具方法的单元测试,以及在内存环境中验证完整MCP协议的集成测试。

When to Use

适用场景

  • Adding automated tests to an MCP server
  • Testing individual tool methods with mocked dependencies
  • Writing integration tests that validate tool listing and invocation via MCP protocol
  • Setting up CI test pipelines for MCP servers
  • 为MCP服务器添加自动化测试
  • 通过模拟依赖项测试单个工具方法
  • 编写集成测试,验证通过MCP协议进行的工具列表查询与调用
  • 为MCP服务器搭建CI测试流水线

Stop Signals

注意事项

  • No server yet? → Use
    mcp-csharp-create
    first
  • Server not running? → Use
    mcp-csharp-debug
  • Just need manual/interactive testing? → Use
    mcp-csharp-debug
    for MCP Inspector
  • 还没有服务器? → 先使用
    mcp-csharp-create
  • 服务器未运行? → 使用
    mcp-csharp-debug
  • 仅需手动/交互式测试? → 使用
    mcp-csharp-debug
    中的MCP Inspector

Inputs

输入参数

InputRequiredDescription
MCP server project pathYesPath to the server
.csproj
being tested
Test frameworkRecommendedDefault: xUnit. Also supports NUnit or MSTest
Transport typeRecommendedDetermines integration test approach (stdio vs HTTP)
输入项是否必填描述
MCP服务器项目路径待测试服务器的
.csproj
文件路径
测试框架推荐默认:xUnit。同时支持NUnit或MSTest
传输类型推荐决定集成测试的实现方式(stdio vs HTTP)

Workflow

工作流程

Step 1: Create the test project

步骤1:创建测试项目

bash
dotnet new xunit -n <ServerName>.Tests
cd <ServerName>.Tests
dotnet add reference ../<ServerName>/<ServerName>.csproj
dotnet add package ModelContextProtocol
dotnet add package Moq
dotnet add package FluentAssertions
bash
dotnet new xunit -n <ServerName>.Tests
cd <ServerName>.Tests
dotnet add reference ../<ServerName>/<ServerName>.csproj
dotnet add package ModelContextProtocol
dotnet add package Moq
dotnet add package FluentAssertions

Step 2: Write unit tests for tool methods

步骤2:编写工具方法的单元测试

Test tool methods directly — fastest and most isolated:
csharp
public class MyToolTests
{
    [Fact]
    public void Echo_ReturnsFormattedMessage()
    {
        var result = MyTools.Echo("Hello");
        result.Should().Be("Echo: Hello");
    }

    [Theory]
    [InlineData("")]
    [InlineData("   ")]
    public void Echo_HandlesEdgeCases(string input)
    {
        var result = MyTools.Echo(input);
        result.Should().StartWith("Echo:");
    }
}
For tools with DI dependencies, mock the dependency:
csharp
public class ApiToolTests
{
    [Fact]
    public async Task FetchData_ReturnsApiResponse()
    {
        var handler = new MockHttpMessageHandler("""{"id": 1}""");
        var httpClient = new HttpClient(handler);

        var result = await ApiTools.FetchData(httpClient, "resource-1");
        result.Should().Contain("id");
    }
}
直接测试工具方法——速度最快且隔离性最好:
csharp
public class MyToolTests
{
    [Fact]
    public void Echo_ReturnsFormattedMessage()
    {
        var result = MyTools.Echo("Hello");
        result.Should().Be("Echo: Hello");
    }

    [Theory]
    [InlineData("")]
    [InlineData("   ")]
    public void Echo_HandlesEdgeCases(string input)
    {
        var result = MyTools.Echo(input);
        result.Should().StartWith("Echo:");
    }
}
对于依赖DI的工具,模拟依赖项:
csharp
public class ApiToolTests
{
    [Fact]
    public async Task FetchData_ReturnsApiResponse()
    {
        var handler = new MockHttpMessageHandler("""{"id": 1}""");
        var httpClient = new HttpClient(handler);

        var result = await ApiTools.FetchData(httpClient, "resource-1");
        result.Should().Contain("id");
    }
}

Step 3: Write integration tests with MCP client

步骤3:使用MCP客户端编写集成测试

Test the full MCP protocol using a client-server connection:
csharp
using ModelContextProtocol.Client;

public class ServerIntegrationTests : IAsyncLifetime
{
    private McpClient _client = null!;

    public async Task InitializeAsync()
    {
        var transport = new StdioClientTransport(new StdioClientTransportOptions
        {
            Name = "TestClient",
            Command = "dotnet",
            Arguments = ["run", "--project", "../<ServerName>/<ServerName>.csproj"]
        });
        _client = await McpClient.CreateAsync(transport);
    }

    public async Task DisposeAsync() => await _client.DisposeAsync();

    [Fact]
    public async Task Server_ListsExpectedTools()
    {
        var tools = await _client.ListToolsAsync();
        tools.Should().Contain(t => t.Name == "echo");
    }

    [Fact]
    public async Task Tool_ReturnsExpectedResult()
    {
        var result = await _client.CallToolAsync("echo",
            new Dictionary<string, object?> { ["message"] = "Test" });
        var text = result.Content.OfType<TextContentBlock>().First().Text;
        text.Should().Contain("Test");
    }
}
For the SDK's
ClientServerTestBase
(in-memory testing) and HTTP testing with
WebApplicationFactory
, see references/test-patterns.md.
通过客户端-服务器连接测试完整的MCP协议:
csharp
using ModelContextProtocol.Client;

public class ServerIntegrationTests : IAsyncLifetime
{
    private McpClient _client = null!;

    public async Task InitializeAsync()
    {
        var transport = new StdioClientTransport(new StdioClientTransportOptions
        {
            Name = "TestClient",
            Command = "dotnet",
            Arguments = ["run", "--project", "../<ServerName>/<ServerName>.csproj"]
        });
        _client = await McpClient.CreateAsync(transport);
    }

    public async Task DisposeAsync() => await _client.DisposeAsync();

    [Fact]
    public async Task Server_ListsExpectedTools()
    {
        var tools = await _client.ListToolsAsync();
        tools.Should().Contain(t => t.Name == "echo");
    }

    [Fact]
    public async Task Tool_ReturnsExpectedResult()
    {
        var result = await _client.CallToolAsync("echo",
            new Dictionary<string, object?> { ["message"] = "Test" });
        var text = result.Content.OfType<TextContentBlock>().First().Text;
        text.Should().Contain("Test");
    }
}
关于SDK的
ClientServerTestBase
(内存测试)和使用
WebApplicationFactory
的HTTP测试
,请查看 references/test-patterns.md

Step 4: Run tests

步骤4:运行测试

bash
undefined
bash
undefined

Run all tests

运行所有测试

dotnet test
dotnet test

Run a specific test class

运行指定测试类

dotnet test --filter "FullyQualifiedName~MyToolTests"
dotnet test --filter "FullyQualifiedName~MyToolTests"

Run with coverage

带覆盖率运行测试

dotnet test --collect:"XPlat Code Coverage"
undefined
dotnet test --collect:"XPlat Code Coverage"
undefined

Step 5: Write evaluations

步骤5:编写评估用例

Evaluations measure how well an LLM uses your tools. Good evaluation questions should be:
  • Read-only and non-destructive — never modify data as a side effect
  • Deterministic — have a single verifiable correct answer
  • Multi-step — require the LLM to call multiple tools or reason across results
For the evaluation format, example questions, and detailed guidance, see references/evaluations.md.
评估用例用于衡量LLM使用工具的效果。优质的评估问题应满足:
  • 只读且无破坏性 —— 不会因副作用修改数据
  • 确定性 —— 具备唯一可验证的正确答案
  • 多步骤 —— 要求LLM调用多个工具或跨结果进行推理
关于评估格式、示例问题和详细指南,请查看 references/evaluations.md

Validation

验证清单

  • Unit tests cover all tool methods, including edge cases
  • Integration tests verify tool listing via
    ListToolsAsync()
  • Integration tests verify tool invocation via
    CallToolAsync()
  • All tests pass:
    dotnet test
  • Tests run in CI without manual setup
  • 单元测试覆盖所有工具方法,包括边缘情况
  • 集成测试验证通过
    ListToolsAsync()
    查询工具列表的功能
  • 集成测试验证通过
    CallToolAsync()
    调用工具的功能
  • 所有测试通过:
    dotnet test
  • 测试可在CI环境中无需手动配置即可运行

Common Pitfalls

常见问题与解决方案

PitfallSolution
Integration test hangs on
CreateAsync
Server fails to start. Verify
dotnet build
succeeds first. For stdio, ensure no stdout logging
StdioClientTransport
not finding project
Use the correct relative path to
.csproj
from the test project directory
Tests pass locally but fail in CIRun
dotnet build
before test execution. Use
--no-build
only after an explicit build step
Mocking
HttpClient
is awkward
Mock
HttpMessageHandler
, not
HttpClient
directly. See references/test-patterns.md
Full test suite runs are slowUse
--filter
for development. Run the full suite only for CI verification
问题解决方案
集成测试在
CreateAsync
处挂起
服务器启动失败。先验证
dotnet build
执行成功。对于stdio传输,确保无stdout日志输出
StdioClientTransport
找不到项目
使用从测试项目目录到
.csproj
的正确相对路径
本地测试通过但CI环境测试失败在执行测试前先运行
dotnet build
。仅在显式构建步骤后使用
--no-build
模拟
HttpClient
操作繁琐
直接模拟
HttpMessageHandler
,而非
HttpClient
。请查看 references/test-patterns.md
完整测试套件运行缓慢开发阶段使用
--filter
筛选测试。仅在CI验证时运行完整套件

Related Skills

相关技能

  • mcp-csharp-create
    — Create a new MCP server project
  • mcp-csharp-debug
    — Running and interactive debugging
  • mcp-csharp-publish
    — NuGet, Docker, Azure deployment
  • mcp-csharp-create
    —— 创建新的MCP服务器项目
  • mcp-csharp-debug
    —— 运行与交互式调试
  • mcp-csharp-publish
    —— NuGet、Docker、Azure部署

Reference Files

参考文件

  • references/test-patterns.md — Complete test code examples:
    ClientServerTestBase
    in-memory pattern,
    WebApplicationFactory
    for HTTP,
    MockHttpMessageHandler
    helper, test categorization, coverage reporting. Load when: writing integration tests or need detailed mock patterns.
  • references/evaluations.md — Evaluation format, question design principles, and example eval questions. Load when: user asks about evaluations, eval questions, or measuring tool quality.
  • references/test-patterns.md —— 完整的测试代码示例:
    ClientServerTestBase
    内存测试模式、用于HTTP测试的
    WebApplicationFactory
    MockHttpMessageHandler
    辅助类、测试分类、覆盖率报告。加载时机: 编写集成测试或需要详细模拟模式时。
  • references/evaluations.md —— 评估格式、问题设计原则和示例评估问题。加载时机: 用户询问评估、评估问题或工具质量衡量相关内容时。

More Info

更多信息