Skip to content

Commit bbe7b6f

Browse files
committed
添加测试规范和已测用例
1 parent aa1d2fa commit bbe7b6f

2 files changed

Lines changed: 295 additions & 0 deletions

File tree

docs/knowledge/test-cases.md

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# 测试用例规划
2+
3+
本文档详细列出了 `DotNetCampus.ModelContextProtocol` 仓库应实现的单元测试用例,按功能模块和测试文件进行组织。
4+
5+
> **状态图例**: ✅ 已实现 | ⏳ 占位(待功能完成) | 📋 规划中
6+
7+
## 1. 核心功能测试 (Core Integration)
8+
9+
**文件路径**: `tests/DotNetCampus.ModelContextProtocol.Tests/Clients/CoreTests.cs`
10+
**目标**: 使用 HTTP 传输(LocalHost 和 TouchSocket)验证 Client 与 Server 的完整交互流程。这是测试覆盖率的主力。
11+
12+
### 1.1 初始化与握手 (Handshake)
13+
| 状态 | 方法名 | DataRow / 参数 | 预期行为 |
14+
| :---: | :--- | :--- | :--- |
15+
|| `Initialize` | `LocalHost`, `TouchSocket` | 成功握手,返回 ServerInfo 和 Capabilities |
16+
|| `Ping` | `LocalHost`, `TouchSocket` | 连接状态正常,IsConnected 为 true |
17+
18+
### 1.2 工具调用 (Tools)
19+
**测试前提**: Server 端注册了 Calculator (`add`/`divide`), Echo (`echo`/`echo_user`), LongText (`generate`), Exception (`throw_error`/`throw_nested`) 等测试工具。
20+
21+
> **注意**: 工具名称使用 snake_case 格式(由源生成器自动从 PascalCase 方法名转换)。
22+
23+
| 状态 | 方法名 | DataRow / 参数 | 预期行为 |
24+
| :---: | :--- | :--- | :--- |
25+
|| `ListTools` | `LocalHost`, `TouchSocket` | 返回工具列表,验证包含 `add`, `echo`, `throw_error`, `generate` |
26+
|| `CallTool` | `Tool="add", Args={a=10, b=20}` | 返回 `content[0].text == "30"`, `isError == false` |
27+
|| `CallTool_ComplexObject` | `Tool="echo_user", Args={user={Name="TestUser", Id=123}}` | 能正确传递并返回复杂 JSON 对象 |
28+
|| `CallTool_MissingArgs` | `Tool="add", Args={a=10}` | 返回 `isError == true`, message 包含参数缺失提示 |
29+
| 📋 | `CallTool_InvalidArgType` | `Tool="add", Args={a="bad", b=2}` | 返回 `isError == true`, message 包含类型错误提示 |
30+
|| `CallTool_ImplementationThrows` | `Tool="throw_error"` | 返回 `isError == true`, message 包含异常信息 |
31+
|| `CallTool_LongOutput` | `Tool="generate", Length=100KB` | 成功接收完整长文本,无截断或内存溢出 |
32+
|| `CallTool_NonExistent` | `Tool="non_existent_tool"` | 返回 `isError == true`,提示工具不存在 |
33+
|| `CallTool_Echo` | `Tool="echo", Args={message="Hello"}` | 返回原始消息 |
34+
35+
### 1.3 资源访问 (Resources)
36+
**测试前提**: Server 端注册了 `SimpleResource` 测试资源提供者。
37+
38+
| 状态 | 方法名 | DataRow / 参数 | 预期行为 |
39+
| :---: | :--- | :--- | :--- |
40+
|| `ListResources` | `LocalHost`, `TouchSocket` | 返回资源列表,验证包含 `test://file1`, `test://image.png` |
41+
|| `ReadResource_TextFile` | `Uri="test://file1"` | 返回 TextResourceContents, 验证内容正确 |
42+
|| `ReadResource_BinaryFile` | `Uri="test://image.png"` | 返回 BlobResourceContents, 验证 Base64 编码正确 |
43+
| 📋 | `ReadResource_NonExistent` | `Uri="test://404"` | Server 返回错误响应 |
44+
|| `ReadResource_WithTemplate` | `Uri="test://users/123/profile"` | 正确匹配 UriTemplate 并返回动态资源 |
45+
46+
### 1.4 提示词 (Prompts)
47+
> ⚠️ **注意**: Server 端 Prompts 功能尚未完全实现,以下测试为占位符。
48+
49+
| 状态 | 方法名 | DataRow / 参数 | 预期行为 |
50+
| :---: | :--- | :--- | :--- |
51+
|| `ListPrompts` | - | 返回 Prompts 列表 |
52+
|| `GetPrompt` | `Name="SimplePrompt"` | 返回 Messages 列表 |
53+
| 📋 | `GetPrompt_WithArguments` | `Name="TemplatePrompt", Args={topic="Code"}` | 返回内容中包含替换后的 "Code" |
54+
55+
---
56+
57+
## 2. 传输层测试 (Transport Layer)
58+
59+
**文件路径**: `tests/DotNetCampus.ModelContextProtocol.Tests/Transports/TransportTests.cs`
60+
**目标**: 验证不同传输实现的连接建立、断开及特殊传输特性。
61+
62+
### 2.1 协议无关性测试 (Polymorphism)
63+
使用 `[DataRow]` 同时测试多种 Transport。
64+
65+
| 状态 | 方法名 | DataRow / 参数 | 预期行为 |
66+
| :---: | :--- | :--- | :--- |
67+
|| `Connect` | `LocalHost`, `TouchSocket` | HTTP 连接成功,IsConnected 为 true |
68+
|| `Disconnect` | `LocalHost`, `TouchSocket` | 连接断开,Dispose 释放资源无异常 |
69+
| 📋 | `Connect` | `StdioMock` | 内存流模拟连接成功 |
70+
| 📋 | `Connect` | `InProcess` | 内存直连成功 |
71+
72+
### 2.2 Stdio 特性测试
73+
**文件路径**: `tests/DotNetCampus.ModelContextProtocol.Tests/Transports/StdioTransportTests.cs`
74+
**目标**: 针对流式传输特有的分包、粘包、Header 解析进行测试。
75+
76+
> ⚠️ **注意**: Stdio 传输层尚未完全实现,以下测试为占位符。
77+
78+
| 状态 | 方法名 | 场景描述 | 预期行为 |
79+
| :---: | :--- | :--- | :--- |
80+
|| `Placeholder` | - | 占位测试 |
81+
| 📋 | `Receive_ChunkedJson` | 将一个 JSON 报文拆成多个 byte 数组分次写入 Stream | 能够完整拼合包并解析出消息 |
82+
| 📋 | `Receive_StickyPacket` | 将多个 JSON 报文一次性写入 Stream (粘包) | 能够依次触发多次消息接收逻辑 |
83+
| 📋 | `Receive_InvalidHeader` | 写入错误的 `Content-Length` | 抛出协议异常或断开连接 |
84+
85+
### 2.3 HTTP 特性测试
86+
**文件路径**: `tests/DotNetCampus.ModelContextProtocol.Tests/Transports/HttpTransportTests.cs`
87+
**目标**: 针对 SSE 和 POST 通信模式的特定测试。
88+
89+
| 状态 | 方法名 | 场景描述 | 预期行为 |
90+
| :---: | :--- | :--- | :--- |
91+
|| `Delete_TerminateSession` | `LocalHost`, `TouchSocket` | DELETE 请求成功终止会话,IsConnected 为 false |
92+
|| `Post_NoSessionId` | 不带 `sessionId` query 发送消息 | 返回 400 Bad Request 或相应错误 |
93+
|| `Sse_EndpointEvent` | 建立旧协议 SSE 连接 | 首先收到 `event: endpoint` 消息 |
94+
95+
---
96+
97+
## 3. 官方兼容性测试 (Compliance)
98+
99+
**文件路径**: `tests/DotNetCampus.ModelContextProtocol.Tests/Compliance/OfficialServerTests.cs`
100+
**目标**: 启动真正的 Node.js MCP Server 验证本库 Client。
101+
102+
> ⚠️ **注意**: 需要 Stdio 传输层和 Node.js 环境,以下测试为占位符。
103+
104+
| 状态 | 方法名 | 场景描述 | 预期行为 |
105+
| :---: | :--- | :--- | :--- |
106+
|| `Connect_ToFilesystemServer` | 启动 `@modelcontextprotocol/server-filesystem` | 成功 Initialize |
107+
|| `ListResources_FromFilesystem` | 配置 Server 访问特定测试目录 | 返回目录下的文件列表 |
108+
|| `ReadResource_FileContent` | 读取已知文本文件 | 内容与磁盘文件一致 |
109+
110+
---
111+
112+
## 4. 已实现的辅助工具
113+
114+
### 4.1 测试工具 (Test Tools)
115+
**文件路径**: `tests/DotNetCampus.ModelContextProtocol.Tests/McpTools/`
116+
117+
| 文件 | 类名 | 工具方法 | 用途 |
118+
| :--- | :--- | :--- | :--- |
119+
| `CalculatorTool.cs` | `CalculatorTool` | `Add(int a, int b)`, `Divide(int a, int b)` | 基本计算测试 |
120+
| `EchoTool.cs` | `EchoTool` | `Echo(string message)`, `EchoUser(EchoUserInfo user)` | 回显和复杂对象测试 |
121+
| `ExceptionTool.cs` | `ExceptionTool` | `ThrowError(string? message)`, `ThrowNested()` | 异常处理测试 |
122+
| `LongTextTool.cs` | `LongTextTool` | `Generate(int length)` | 大数据量测试 |
123+
| `SimpleTool.cs` | `SimpleTool` | `SayHello()` | 最简单的工具 |
124+
125+
### 4.2 测试资源 (Test Resources)
126+
**文件路径**: `tests/DotNetCampus.ModelContextProtocol.Tests/McpResources/`
127+
128+
| 文件 | 类名 | 资源方法 | 用途 |
129+
| :--- | :--- | :--- | :--- |
130+
| `SimpleResource.cs` | `SimpleResource` | `TextFile()`, `BinaryImage()`, `UserProfile(int userId)` | 基本资源访问测试 |
131+
132+
### 4.3 测试工厂 (Integration Factory)
133+
**文件路径**: `tests/DotNetCampus.ModelContextProtocol.Tests/TestMcpFactory.cs`
134+
135+
| 方法 | 用途 |
136+
| :--- | :--- |
137+
| `CreateSimpleHttpAsync(HttpTransportType)` | 创建仅包含 SimpleTool 的测试包 |
138+
| `CreateFullHttpAsync(HttpTransportType)` | 创建包含所有测试工具的测试包 |
139+
| `CreateFullHttpWithResourcesAsync(HttpTransportType)` | 创建包含工具和资源的完整测试包 |
140+
| `CreateHttpCoreAsync(HttpTransportType, Action<McpServerBuilder>)` | 完全自定义的测试包创建 |
141+
142+
### 4.4 JSON 序列化上下文
143+
**文件路径**: `tests/DotNetCampus.ModelContextProtocol.Tests/McpTools/TestToolJsonContext.cs`
144+
145+
用于 AOT 兼容的复杂对象序列化,包含 `EchoUserInfo` 等类型的注册。
146+
147+
---
148+
149+
## 5. 待开发的辅助工具
150+
151+
1. **Mock Transport**
152+
* `InProcessServerTransport` / `InProcessClientTransport`
153+
* `MemoryStreamServerTransport` (用于 Stdio 模拟)
154+
155+
2. **更多测试资源**
156+
* `DictionaryResourceProvider`: 基于 `Dictionary<Uri, string>` 的简单资源提供者
157+
158+
---
159+
160+
## 6. 测试统计
161+
162+
| 类别 | 通过 | 跳过 | 规划 |
163+
| :--- | :---: | :---: | :---: |
164+
| 核心功能测试 | 28 | 2 | 2 |
165+
| 传输层测试 | 6 | 3 | 4 |
166+
| 官方兼容性测试 | 0 | 3 | 0 |
167+
| **总计** | **34** | **8** | **6** |

docs/knowledge/testing-guide.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# 单元测试与质量保证指南
2+
3+
本文档旨在指导 `DotNetCampus.ModelContextProtocol` 项目的测试工作,确保代码质量并符合 MCP 协议标准。
4+
5+
## 1. 测试范围设计
6+
7+
为了确保库的健壮性和协议符合性,我们需要覆盖以下层面的测试:
8+
9+
### 1.1 核心逻辑与集成 (Integration/End-to-End)
10+
使用本库的 Server 和 Client 在同一进程内进行端到端交互。这是我们测试的重心。
11+
* **握手流程**: `Initialize` -> `Initialized`
12+
* **主要能力测试**:
13+
* **Tools**: ListTools, CallTool (包括参数验证, 异常处理, 长文本返回等)。
14+
* **Resources**: ListResources, ReadResource, Subscription。
15+
* **Prompts**: ListPrompts, GetPrompt。
16+
* **通知机制**: Logging, Progress, 资源变更通知。
17+
18+
### 1.2 传输层 (Transport Layer)
19+
针对 `src/DotNetCampus.ModelContextProtocol/Transports` 及具体实现。
20+
* **多种实现覆盖**
21+
* **HTTP**: 覆盖 `LocalHost``TouchSocket` 实现。
22+
* **Stdio**: 覆盖标准输入输出流传输(这是 MCP 最常用的模式)。
23+
* **In-Process**: (规划中)直接在内存中传递消息,用于极速测试核心逻辑。
24+
* **会话管理**
25+
* 连接建立 (Connection management)。
26+
* 消息分片/粘包处理 (针对 Stdio/Stream 传输)。
27+
* Cancellation 和 Timeout 处理。
28+
29+
### 1.3 兼容性测试 (Compliance)
30+
* **官方服务验证**
31+
* 验证本库 Client 能否正确连接并调用 **官方 MCP SDK (Node.js/Python)** 编写的 Server。
32+
* 验证本库 Server 能否被官方 MCP Client 连接。
33+
* **协议正确性**:通过与官方 SDK 的交互,间接验证 JSON 序列化/反序列化、协议字段定义的正确性(不再单独编写序列化测试)。
34+
35+
## 2. 测试规范
36+
37+
### 2.1 框架与工具
38+
* **测试框架**: MSTest
39+
* **断言库**: MSTest 自带 Assert
40+
* **Mock 策略**:
41+
* 优先使用 **In-Process Integration Tests** (即真实的 Server + 真实的 Client),因为 MCP 逻辑重在交互。
42+
* 对于难以触发的异常路径,可适当使用 Mock `ITransport` 或特定的 Fake Tool。
43+
44+
### 2.2 代码编写规范
45+
46+
#### 复用性设计
47+
利用 `[DataRow]``[DynamicData]` 在不同配置下运行相同的测试逻辑。
48+
49+
**示例:同时测试多种 HTTP 实现**
50+
```csharp
51+
[TestMethod]
52+
[DataRow(HttpTransportType.LocalHost)]
53+
[DataRow(HttpTransportType.TouchSocket)]
54+
public async Task ListTools(HttpTransportType transportType)
55+
{
56+
await using var package = await TestMcpFactory.Shared.CreateSimpleHttpAsync(transportType);
57+
var result = await package.Client.ListToolsAsync();
58+
Assert.IsNotNull(result);
59+
}
60+
```
61+
62+
#### 命名规范
63+
* **测试类**: `{Feature}Tests``{Component}Tests`,例如 `ToolExecutionTests`.
64+
* **测试方法**: 使用简单清晰的方法名,配合 `DisplayName` 说明测试目的。必要时可使用后缀区分场景(如 `_Error`, `01` 等)。
65+
* 例: `CallTool` (配合 `[DataRow]`), `CallTool_Exception`
66+
* 避免过长的 `Method_State_Expected` 命名风格。
67+
68+
## 3. 测试辅助设施 (Test Infrastructure)
69+
70+
为了方便编写测试,我们需要完善 `DotNetCampus.ModelContextProtocol.Tests` 项目中的辅助工具。
71+
72+
### 3.1 扩充 `TestMcpFactory`
73+
目前的 `TestMcpFactory` 需进一步扩充:
74+
75+
1. **Fluent Configuration**: 允许测试用例自定义 Server 挂载的 Tools/Resources。
76+
```csharp
77+
// 理想的测试代码写法
78+
var package = await TestMcpFactory.Create()
79+
.WithTools(tools => tools.Add("calculate", (int a, int b) => a + b))
80+
.UseTransport(transport => transport.InternalMemory) // 内存直连
81+
.BuildAsync();
82+
```
83+
2. **特定的测试工具集 (Test Tools)**:
84+
* 创建多种预置的 Tool 供测试使用:
85+
* `ExceptionTool`: 总是抛出异常,测试错误处理。
86+
* `LongTextTool`: 返回超长文本,测试分片或性能。
87+
* `EchoTool`: 原样返回输入,测试复杂对象传参。
88+
89+
### 3.2 传输层模拟方案
90+
91+
#### 内存传输 (In-Process Transport)
92+
实现 `InProcessServerTransport` 和 `InProcessClientTransport`。
93+
* 利用字符串 Key 或共享内存对象进行配对。
94+
* **优势**: 极快,无网络/IO开销,适合大量逻辑测试。
95+
* **未来**: 成熟后的代码可移入主库供用户使用。
96+
97+
#### Stdio 传输模拟
98+
改造现有的 `StdioServerTransport` 和 `StdioClientTransport`。
99+
* **构造函数注入**: 允许传入 `Stream` (StandardInput/StandardOutput) 而非硬编码 Console
100+
* **测试方式**: 在测试中使用 `MemoryStream` 或 `PipeStream` 连接 ServerClient 实例,无需启动外部子进程即可测试流式协议逻辑(如 Header 解析、粘包处理)。
101+
102+
### 3.3 官方 Server 启动器 (`OfficialServerFixture`)
103+
编写一个帮助类,用于启动外部 Node.js 进程运行官方示例 Server
104+
* **假设**: CI/开发环境已安装 `npm`/`npx`。
105+
* **功能**: 启动 `npx -y @modelcontextprotocol/server-filesystem`,并提供连接到该进程的标准输入输出的 Transport
106+
107+
## 4. 建议的测试开发路线图 (Todo List)
108+
109+
### Phase 1: 核心功能验证 (Priority High)
110+
覆盖 Client 调用 Server 的主流程。
111+
- [ ] **Client/ToolTests.cs**:
112+
- [ ] 构造 `ExceptionTool` 等辅助工具。
113+
- [ ] 测试无参、简单参数、复杂对象参数的 Tool 调用。
114+
- [ ] 测试 Tool 执行抛出异常时,Client 端收到的结果(应包含 `isError: true`)。
115+
- [ ] **Client/LifecycleTests.cs**:
116+
- [ ] 显式测试 `InitializeAsync` 和版本协商。
117+
- [ ] 测试 `PingAsync`。
118+
119+
### Phase 2: 传输层增强 (Priority Medium)
120+
- [ ] **TestInfrastructure**:
121+
- [ ] 实现 `InProcessTransport` 并验证其可靠性。
122+
- [ ] 改造 Stdio Transport 支持 Stream 注入。
123+
- [ ] **Transport/StdioTransportTests.cs**:
124+
- [ ] 使用内存流模拟 Stdio,验证 `StreamJsonRpc` 或自定义流读写的粘包/分片处理能力。
125+
126+
### Phase 3: 官方兼容性 (Priority Low/Validation)
127+
- [ ] **Compliance/OfficialIntegrationTests.cs**:
128+
- [ ] 尝试连接官方 `filesystem` Server 并读取目录。

0 commit comments

Comments
 (0)