如何用NUnit测试Catch块满足Sonar覆盖率?测试失败求解
问题:NUnit测试Catch块覆盖率失败,报错“Expected: <System.Exception> But was: null”
我是NUnit(3.4+)新手,希望编写NUnit测试用例确保代码中的Catch块被Sonar覆盖率统计到,但尝试的方案均失败,报错“Expected: <System.Exception> But was: null”。
被测方法代码:
public async Task<List<ReturnObjects>> GetData(int Id) { try { // 实际代码会调用_queryHelperDataMart.WrapQueryAsync } catch(Exception) { // 处理异常逻辑,未重新抛出 } }
我的测试用例代码:
[Test] public async Task CanCallGetData_Test_ThrowsException() { // Arrange var Id = 927810493; List<ReturnObjects> objs = null; //尝试通过空引用触发异常 //模拟仓储类数据 _queryHelperDataMart.Setup(mock => mock.WrapQueryAsync<ReturnObjects> (It.IsAny<string>(), It.IsAny<IConfiguration>(), It.IsAny<Dictionary<string, object>>(), It.IsAny<string>())).ReturnsAsync(objs); // Act var result = Assert.ThrowsAsync<Exception>(() => _testClass.GetData(Id)); // Assert Assert.That(result, ..check exception etc); }
请问我哪里出错了?另外,当无法从测试方法生成实际异常时,该如何编写测试用例覆盖Catch块?
解答
错误原因分析
- 测试方向错误:被测方法的Catch块没有重新抛出异常,方法执行时即使进入Catch块,也不会向外抛出异常。但你用了
Assert.ThrowsAsync<Exception>,它期望方法会抛出异常,自然会失败。 - 未真正触发异常:你让Mock返回
null,但如果被测方法的try块里没有代码会因为null抛出异常(比如空的try块,或者调用WrapQueryAsync返回null后没有后续操作),根本不会进入Catch块。
修正方案
1. 让Mock抛出异常,触发Catch块
要进入Catch块,必须让try块内的代码抛出异常。把Mock的ReturnsAsync(objs)改成ThrowsAsync,直接抛出异常:
// Arrange部分修正 _queryHelperDataMart.Setup(mock => mock.WrapQueryAsync<ReturnObjects>( It.IsAny<string>(), It.IsAny<IConfiguration>(), It.IsAny<Dictionary<string, object>>(), It.IsAny<string>())) .ThrowsAsync(new Exception("测试异常"));
2. 调整测试逻辑,适配方法吞异常的行为
因为被测方法不会向外抛异常,所以不能用Assert.ThrowsAsync,直接调用方法后验证其行为:
[Test] public async Task GetData_WhenThrowsException_CoversCatchBlock() { // Arrange var Id = 927810493; _queryHelperDataMart.Setup(mock => mock.WrapQueryAsync<ReturnObjects>( It.IsAny<string>(), It.IsAny<IConfiguration>(), It.IsAny<Dictionary<string, object>>(), It.IsAny<string>())) .ThrowsAsync(new Exception()); // Act var result = await _testClass.GetData(Id); // Assert // 根据Catch块的实际逻辑验证返回值,比如如果Catch里返回空列表: Assert.That(result, Is.Empty); // 验证Mock方法确实被调用过,确保流程走到了try块 _queryHelperDataMart.Verify(mock => mock.WrapQueryAsync<ReturnObjects>( It.IsAny<string>(), It.IsAny<IConfiguration>(), It.IsAny<Dictionary<string, object>>(), It.IsAny<string>()), Times.Once); }
通用技巧:覆盖Catch块的核心要点
- 确保try块抛出异常:不管是Mock依赖抛出异常,还是构造特定参数触发业务异常,核心是让try块内的代码执行时抛出目标异常类型。
- 不要依赖
Assert.ThrowsAsync:如果被测方法吞了异常,测试重点应放在验证异常处理后的行为(比如返回值、日志、状态变化等),而非捕获向外抛出的异常。 - 用Verify验证流程:通过Moq的
Verify方法确认依赖方法被调用,确保代码确实走到了try块,进而触发Catch块。
内容的提问来源于stack exchange,提问作者MB22
相关产品推荐
相关产品推荐

