|
| 1 | +# 为 Language Server Framework 编写测试的指南 |
| 2 | + |
| 3 | +## 概述 |
| 4 | + |
| 5 | +这个测试框架已经为项目中的主要协议 handler 创建了测试模板。但由于框架的复杂性,需要根据实际的类型定义进行调整。 |
| 6 | + |
| 7 | +## 当前状态 |
| 8 | + |
| 9 | +已创建测试类(需要修正): |
| 10 | +- ✅ CompletionHandlerTests |
| 11 | +- ✅ HoverHandlerTests |
| 12 | +- ✅ DefinitionHandlerTests |
| 13 | +- ✅ DeclarationHandlerTests |
| 14 | +- ✅ ImplementationHandlerTests |
| 15 | +- ✅ TypeDefinitionHandlerTests |
| 16 | +- ✅ ReferenceHandlerTests |
| 17 | +- ✅ DocumentHighlightHandlerTests |
| 18 | +- ✅ DocumentSymbolHandlerTests |
| 19 | +- ✅ CodeActionHandlerTests |
| 20 | +- ✅ CodeLensHandlerTests |
| 21 | +- ✅ DocumentLinkHandlerTests |
| 22 | +- ✅ DocumentColorHandlerTests |
| 23 | +- ✅ DocumentFormattingHandlerTests |
| 24 | +- ✅ RenameHandlerTests |
| 25 | +- ✅ FoldingRangeHandlerTests |
| 26 | +- ✅ SelectionRangeHandlerTests |
| 27 | +- ✅ CallHierarchyHandlerTests |
| 28 | +- ✅ SemanticTokensHandlerTests |
| 29 | +- ✅ InlayHintHandlerTests |
| 30 | +- ✅ SignatureHelpHandlerTests |
| 31 | +- ✅ ExecuteCommandHandlerTests |
| 32 | +- ✅ TextDocumentHandlerTests |
| 33 | + |
| 34 | +## 需要修正的主要问题 |
| 35 | + |
| 36 | +### 1. 类型导入问题 |
| 37 | + |
| 38 | +许多类型需要从正确的命名空间导入: |
| 39 | + |
| 40 | +```csharp |
| 41 | +// 需要添加: |
| 42 | +using EmmyLua.LanguageServer.Framework.Protocol.Model; |
| 43 | +using EmmyLua.LanguageServer.Framework.Protocol.Model.TextEdit; |
| 44 | +``` |
| 45 | + |
| 46 | +### 2. Response 类型结构 |
| 47 | + |
| 48 | +许多 Response 类使用属性而不是独立的集合类: |
| 49 | + |
| 50 | +```csharp |
| 51 | +// 错误: |
| 52 | +response.Locations[0] |
| 53 | + |
| 54 | +// 正确 (可能的实现): |
| 55 | +response.Value // 或其他属性名 |
| 56 | +``` |
| 57 | + |
| 58 | +### 3. Union 类型 |
| 59 | + |
| 60 | +一些属性使用 Union 类型,需要特殊处理: |
| 61 | + |
| 62 | +```csharp |
| 63 | +// 例如 CodeActionProvider 可能是: |
| 64 | +BooleanOr<CodeActionOptions> |
| 65 | + |
| 66 | +// 需要这样访问: |
| 67 | +if (serverCapabilities.CodeActionProvider.HasValue) |
| 68 | +{ |
| 69 | + var options = serverCapabilities.CodeActionProvider.Value; |
| 70 | +} |
| 71 | +``` |
| 72 | + |
| 73 | +### 4. 构造函数参数 |
| 74 | + |
| 75 | +一些类型有必需的构造函数参数: |
| 76 | + |
| 77 | +```csharp |
| 78 | +// 检查类定义: |
| 79 | +public class ExecuteCommandResponse(LSPAny? result) |
| 80 | + |
| 81 | +// 调用时: |
| 82 | +new ExecuteCommandResponse(result: myResult) |
| 83 | +``` |
| 84 | + |
| 85 | +## 如何修正测试 |
| 86 | + |
| 87 | +### 步骤 1: 查看实际的类型定义 |
| 88 | + |
| 89 | +对于每个要测试的 handler,首先查看: |
| 90 | + |
| 91 | +1. Handler 基类的方法签名 |
| 92 | +2. Request 和 Response 类型的定义 |
| 93 | +3. 相关的 Model 类型 |
| 94 | + |
| 95 | +示例: |
| 96 | + |
| 97 | +```csharp |
| 98 | +// 1. 查看 handler |
| 99 | +public abstract class CompletionHandlerBase : IJsonHandler |
| 100 | +{ |
| 101 | + protected abstract Task<CompletionResponse?> Handle(CompletionParams request, CancellationToken token); |
| 102 | +} |
| 103 | + |
| 104 | +// 2. 查看 Response 类型 |
| 105 | +public class CompletionResponse(List<CompletionItem> items) |
| 106 | +{ |
| 107 | + public List<CompletionItem> Items => items; |
| 108 | +} |
| 109 | + |
| 110 | +// 3. 相应地编写测试 |
| 111 | +``` |
| 112 | + |
| 113 | +### 步骤 2: 修正类型引用 |
| 114 | + |
| 115 | +确保所有使用的类型都已正确导入: |
| 116 | + |
| 117 | +```csharp |
| 118 | +using EmmyLua.LanguageServer.Framework.Protocol.Model; |
| 119 | +using EmmyLua.LanguageServer.Framework.Protocol.Model.TextEdit; |
| 120 | +using EmmyLua.LanguageServer.Framework.Protocol.Model.Union; |
| 121 | +``` |
| 122 | + |
| 123 | +### 步骤 3: 修正 Response 访问 |
| 124 | + |
| 125 | +根据实际的 Response 类结构访问数据: |
| 126 | + |
| 127 | +```csharp |
| 128 | +// 查看 Response 类定义来确定正确的属性名 |
| 129 | +var result = await handler.Handle(request, token); |
| 130 | +Assert.NotNull(result); |
| 131 | +Assert.NotEmpty(result.Items); // 或 result.Value, result.Locations 等 |
| 132 | +``` |
| 133 | + |
| 134 | +### 步骤 4: 处理 Union 类型 |
| 135 | + |
| 136 | +对于 Union 类型的属性,使用适当的方法: |
| 137 | + |
| 138 | +```csharp |
| 139 | +// 对于 BooleanOr<T> 类型: |
| 140 | +serverCapabilities.SomeProvider = new BooleanOr<SomeOptions>(new SomeOptions { ... }); |
| 141 | + |
| 142 | +// 或者只是 bool: |
| 143 | +serverCapabilities.SomeProvider = true; |
| 144 | +``` |
| 145 | + |
| 146 | +## 运行单个测试 |
| 147 | + |
| 148 | +在修正每个测试类后,可以单独运行它们: |
| 149 | + |
| 150 | +```bash |
| 151 | +# 运行单个测试类 |
| 152 | +dotnet test --filter "FullyQualifiedName~CompletionHandlerTests" |
| 153 | + |
| 154 | +# 运行单个测试方法 |
| 155 | +dotnet test --filter "FullyQualifiedName~CompletionHandlerTests.Handle_ShouldReturnCompletionItems" |
| 156 | +``` |
| 157 | + |
| 158 | +## 建议的修正顺序 |
| 159 | + |
| 160 | +1. 从简单的 handler 开始 (如 HoverHandler) |
| 161 | +2. 修正一个,运行测试验证 |
| 162 | +3. 逐步处理更复杂的 (如 SemanticTokensHandler, CallHierarchyHandler) |
| 163 | + |
| 164 | +## 示例:修正 CompletionHandlerTests |
| 165 | + |
| 166 | +```csharp |
| 167 | +using EmmyLua.LanguageServer.Framework.Protocol.Capabilities.Client.ClientCapabilities; |
| 168 | +using EmmyLua.LanguageServer.Framework.Protocol.Capabilities.Server; |
| 169 | +using EmmyLua.LanguageServer.Framework.Protocol.Message.Completion; |
| 170 | +using EmmyLua.LanguageServer.Framework.Protocol.Model; |
| 171 | +using EmmyLua.LanguageServer.Framework.Protocol.Model.Kind; |
| 172 | +using EmmyLua.LanguageServer.Framework.Server.Handler; |
| 173 | +using EmmyLua.LanguageServer.Framework.Tests.TestBase; |
| 174 | +using FluentAssertions; |
| 175 | +using Xunit; |
| 176 | + |
| 177 | +namespace EmmyLua.LanguageServer.Framework.Tests.Handlers; |
| 178 | + |
| 179 | +public class CompletionHandlerTests : TestHandlerBase |
| 180 | +{ |
| 181 | + private class TestCompletionHandler : CompletionHandlerBase |
| 182 | + { |
| 183 | + protected override Task<CompletionResponse?> Handle(CompletionParams request, CancellationToken token) |
| 184 | + { |
| 185 | + // 根据实际的 CompletionResponse 构造函数 |
| 186 | + var items = new List<CompletionItem> |
| 187 | + { |
| 188 | + new CompletionItem { Label = "test", Kind = CompletionItemKind.Method } |
| 189 | + }; |
| 190 | + return Task.FromResult<CompletionResponse?>(new CompletionResponse(items)); |
| 191 | + } |
| 192 | + |
| 193 | + protected override Task<CompletionItem> Resolve(CompletionItem item, CancellationToken token) |
| 194 | + { |
| 195 | + return Task.FromResult(item); |
| 196 | + } |
| 197 | + |
| 198 | + public override void RegisterCapability(ServerCapabilities serverCapabilities, ClientCapabilities clientCapabilities) |
| 199 | + { |
| 200 | + // 根据实际的类型设置 |
| 201 | + serverCapabilities.CompletionProvider = /* 正确的值 */; |
| 202 | + } |
| 203 | + } |
| 204 | + |
| 205 | + // ... 测试方法 |
| 206 | +} |
| 207 | +``` |
| 208 | + |
| 209 | +## 额外资源 |
| 210 | + |
| 211 | +- LSP 规范: https://microsoft.github.io/language-server-protocol/ |
| 212 | +- 项目源码中的 Protocol/Message 目录包含所有消息类型定义 |
| 213 | +- 项目源码中的 Server/Handler 目录包含所有 handler 基类 |
| 214 | + |
| 215 | +## 贡献 |
| 216 | + |
| 217 | +如果你成功修正了某个测试类,欢迎提交 PR!请确保: |
| 218 | +1. 测试能够编译通过 |
| 219 | +2. 测试能够成功运行 |
| 220 | +3. 添加适当的注释说明任何特殊处理 |
0 commit comments