ZeroMcp。测试套件--。NET DSL
流利。NET API,用于测试MCP(模型上下文协议)服务器。包裹 mcptest 生锈的发动机和 与xUnit集成,支持Visual Studio测试资源管理器。
快速开始
using ZeroMcp.TestKit;
await McpTest
.Server("http://localhost:8000/mcp")
.Tool("search")
.WithParams(new { query = "hello" })
.ExpectSchemaMatch()
.ExpectDeterministic()
.RunAsync();包裹
| 包装 | 用途 |
|---|---|
ZeroMcp.TestKit | 核心DSL、型号、发动机转轮 |
ZeroMcp.TestKit.Xunit | xUnit属性([McpFact], [McpTheory])以及 McpAssert |
发动机分辨率
这 mcptest 二进制文件按以下顺序自动定位:
MCPTEST_PATH环境变量(二进制文件的完整路径)- NuGet本机资产(
runtimes/{rid}/native/mcptest) - 系统
PATH
每个构建器的覆盖: .WithEnginePath("path/to/mcptest")
流利的API
await McpTest
.Server("http://localhost:8000/mcp")
.WithTimeout(TimeSpan.FromSeconds(30))
.WithDeterminismRuns(5)
.ValidateProtocol()
.ValidateMetadata()
.WithAutoErrorTests()
.Tool("search")
.WithParams(new { query = "hello" })
.ExpectSchemaMatch()
.ExpectDeterministic()
.WithIgnorePaths("$.result.timestamp")
.Tool("echo")
.WithParams(new { text = "world" })
.ExpectSchemaMatch()
.RunAsync();关键方法
McpServerBuilder (从 McpTest.Server(url)):
.WithTimeout(TimeSpan)--全局超时.WithDeterminismRuns(int)--确定性检查的重新运行次数.ValidateProtocol()--启用MCP握手+JSON-RPC帧验证.ValidateMetadata()--验证工具名称、描述、inputSchema.WithAutoErrorTests()--自动生成错误路径测试.Tool(name)--添加工具测试用例
McpToolBuilder (从 .Tool(name)):
.WithParams(object)--工具调用参数.ExpectSchemaMatch()--根据声明的模式验证输出.ExpectDeterministic()--跨运行断言相同的输出.WithIgnorePaths(params string[])--确定性中要跳过的JSONPath字段.ExpectError()/.ExpectErrorCode(long)--误差路径测试.ExpectMinStreamChunks(int)--流验证.WithTimeout(TimeSpan)--每个工具超时覆盖
执行:
.RunAsync()--执行并投掷McpTestException失败时.RunWithoutThrowAsync()--执行并返回结果而不抛出
xUnit 集成
using ZeroMcp.TestKit;
using ZeroMcp.TestKit.Xunit;
public class MyMcpServerTests
{
[McpFact(DisplayName = "search returns valid schema")]
public async Task SearchToolSchemaValid()
{
await McpTest
.Server("http://localhost:8000/mcp")
.Tool("search")
.WithParams(new { query = "hello" })
.ExpectSchemaMatch()
.RunAsync();
}
}McpAssert助手
var result = await McpTest
.Server("http://localhost:8000/mcp")
.Tool("search").WithParams(new { query = "hi" }).ExpectSchemaMatch()
.RunWithoutThrowAsync();
McpAssert.Passed(result);
McpAssert.ToolPassed(result, "search");
McpAssert.SchemaValid(result, "search");
McpAssert.Deterministic(result, "search");响应值断言
引擎现在将原始MCP服务器响应包含在每个工具结果中。使用这些助手 对特定的响应属性和值进行断言:
var result = await McpTest
.Server("http://localhost:8000/mcp")
.Tool("search").WithParams(new { query = "hi" })
.RunWithoutThrowAsync();
// Assert a specific property path has an expected value
McpAssert.ResponseContains(result, "search", "content[0].text", "hello world");
// Assert a property exists at a given path
McpAssert.ResponseHasProperty(result, "search", "content[0].type");
// Get the raw JsonElement for custom assertions
var response = McpAssert.GetResponse(result, "search");
Assert.Equal("text", response.GetProperty("content")[0].GetProperty("type").GetString());流畅的断言链
var result = await McpTest
.Server("http://localhost:8000/mcp")
.Tool("search").WithParams(new { query = "hi" })
.RunWithoutThrowAsync();
result
.Passed()
.HasToolName("search")
.HasValidSchema("search")
.HasReturnProperty("content")
.HasReturnValue("search", "content[0].type", "text");项目结构
dotnet/
├── ZeroMcp.TestKit.slnx
├── src/
│ ├── ZeroMcp.TestKit/ # Core DSL library
│ │ ├── Models/
│ │ │ ├── McpTestDefinition.cs
│ │ │ └── McpTestResult.cs
│ │ ├── McpTest.cs
│ │ ├── McpServerBuilder.cs
│ │ ├── McpToolBuilder.cs
│ │ ├── McpTestRunner.cs
│ │ ├── EngineResolver.cs
│ │ └── McpTestException.cs
│ └── ZeroMcp.TestKit.Xunit/ # xUnit integration
│ ├── McpFluentAssertions.cs
│ ├── McpFactAttribute.cs
│ ├── McpTheoryAttribute.cs
│ └── McpAssert.cs
└── tests/
└── ZeroMcp.TestKit.Tests/ # Unit tests (33 passing)
├── Models/
│ ├── McpTestDefinitionTests.cs
│ └── McpTestResultTests.cs
├── FluentApiTests.cs
├── McpTestExceptionTests.cs
├── McpAssertResponseTests.cs
└── EngineResolverTests.cs建筑
dotnet build
dotnet test需求
- .NET 8.0 SDK
mcptest二进制(见上文发动机分辨率)
许可证
麻省理工学院
