泛型IAsyncEnumerable返回方法代码覆盖率报告存在未覆盖分支的原因排查
泛型IAsyncEnumerable返回方法代码覆盖率报告存在未覆盖分支的原因排查
嘿,我来帮你捋捋这个奇怪的覆盖率问题!其实核心原因和C#编译器处理泛型异步迭代器的方式,以及你当前测试的覆盖范围有关。
先给你拆解清楚:
- 当你写返回
IAsyncEnumerable<T>的异步迭代器方法时,C#编译器会自动生成一个状态机类来实现异步迭代的逻辑,这个状态机里包含了各种分支:正常迭代、取消、异常处理、迭代结束清理等等。 - 对于你的泛型版
ExecuteTable<T>方法,编译器生成的状态机里多了一个分支——专门处理RowConverter.ConvertRow<T>抛出异常的场景,而你目前的所有测试都没有触发这个分支,所以coverlet把它标记为未覆盖。 - 而返回
IAsyncEnumerable<dynamic>的非泛型版方法,要么是RowConverter.ConvertDynamic本身不会抛出异常(或者你的测试场景里不会触发),要么是编译器对dynamic类型的处理逻辑不同,生成的状态机里没有这个额外的异常分支,所以覆盖率是满的。
你看,你的测试覆盖了ExecuteSQL的异常、取消、有无结果这些场景,但唯独没测行转换失败的情况:比如构造一个和TestRecord类型不匹配的Row(比如列名不匹配、字段类型不对),让ConvertRow<T>抛出异常,然后写个测试验证这个异常会被正确传递出来,这样那个未覆盖的分支就会被命中了。
另外还有一个小可能性:coverlet对泛型异步迭代器的IL分析存在一点点偏差,把编译器自动生成的某些“边界清理代码”当成了未覆盖分支,但这种情况概率很低,优先考虑补全转换异常的测试更靠谱。
你可以试试加这么个测试:
[Fact] public async Task ExecuteTable_ConvertRowThrowsException() { // Arrange QueryResponse response = new(); // 构造一个无法转换为TestRecord的列(比如列名不匹配,或者类型不对) response.Columns.Add(new Column() { Name = "WrongColumn", Type = RPCType.String }); var row = new Row(); row.Values.Add(new RPCValue() { StringValue = "Invalid" }); response.Rows.Add(row); _databaseClientWrapperMock.ExecuteQuery(Arg.Any<QueryRequest>(), Arg.Any<CancellationToken>()).Returns(response); var client = CreateDatabaseClient(); // Act & Assert await Assert.ThrowsAsync<InvalidCastException>(async () => { await foreach (var record in client.ExecuteTable<TestRecord>("SELECT * FROM Table", null, TestContext.Current.CancellationToken)) { // 不会走到这里 } }); }
跑这个测试之后,那个未覆盖的分支应该就会被标记为已覆盖了。
备注:内容来源于stack exchange,提问作者F. Polo
相关产品推荐
相关产品推荐

