如何在C# Xunit单元测试中覆盖Rest API的if(days>365)分支
如何修改Xunit单元测试以覆盖Rest API的
days > 365分支并验证逻辑 问题背景
我有一个Rest API接口,其Run方法包含if (days > 365)的逻辑分支,但当前的Xunit单元测试既没覆盖该分支,也没验证分支执行逻辑,仅验证了模拟返回的假数据。需要调整测试来覆盖该分支并确认逻辑正确性。
API核心代码
[HttpPost, Route("v1/test")] public async Task<IActionResult> Run(Request request) { var days = (request.ToDate - request.FromDate).TotalDays; var today = new MonthMap(request.ToDate.UtcDateTime.Month, request.ToDate.UtcDateTime.Day); var fromDay = new MonthMap(request.FromDate.UtcDateTime.Month, request.FromDate.UtcDateTime.Day); if (days > 365) { fromDay = new MonthMap(1, 1); today = new MonthMap(12, 31); } var query = new Query { ToDay = today, FromDay = fromDay }; var currentUtcTime = DateTimeOffset.UtcNow; var results = await _valueService.GetResult(query, currentUtcTime); return new OkObjectResult(results); } public MonthMap(int month, int day) { if (month > 12 || month < 1) { throw new ArgumentException(" number between 1 and 12"); } if (day < 1 || day > 31) { throw new ArgumentException("number between 1 and 31"); } Day = day; Month = month; }
当前单元测试代码
[Fact] public async Task Test_Run_When_Day_Greater_Than_365() { // Arrange var request = new Request { FromDate = new DateTimeOffset(2022, 1, 1, 0, 0, 0, TimeSpan.Zero), ToDate = new DateTimeOffset(2022, 12, 31, 0, 0, 0, TimeSpan.Zero), }; var mockValueService = new Mock<IValueService>(); var mockLogger = new Mock<ILogger<ReportsController>>(); var controller = new ReportsController(mockValueService.Object, mockLogger.Object); mockValueService.Setup(x => x.GetResult(It.IsAny<Query>(),It.IsAny<DateTimeOffset>())) .ReturnsAsync(new List<MonthLyDataModel> {new IresultModel{ Id = "1", Result= "CO1", Month= "1" }, new IresultModel{ Id = "1", Result= "CO1", Month= "2" }, new IresultModel{ Id = "1", Result= "CO1", Month= "3" }, new IresultModel{ Id = "1", Result= "CO1", Month= "4" }, new IresultModel{ Id = "1", Result= "CO1", Month= "5" }, new IresultModel{ Id = "1", Result= "CO1", Month= "6" }, new IresultModel{ Id = "1", Result= "CO1", Month= "7" }, new IresultModel{ Id = "1", Result= "CO1", Month= "8" }, new IresultModel{ Id = "1", Result= "CO1", Month= "9" }, new IresultModel{ Id = "1", Result= "CO1", Month= "10" }, new IresultModel{ Id = "1", Result= "CO1", Month= "11" }, new IresultModel{ Id = "1", Result= "CO1", Month= "12" }, }); // Act var result = await controller.Run(request); Assert.IsType<OkObjectResult>(result); var resultObject = (OkObjectResult)result; var resultJson = (List<MonthLyDataModel>)resultObject.Value; Assert.Equal(resultJson.Count, 12); }
当前测试的问题:传入的日期差不足365天(2022年1月1日到12月31日是364天),不会触发days > 365分支;且仅验证返回数据数量,未检查分支执行时Query对象的FromDay和ToDay是否符合预期。
修改后的单元测试方案
1. 调整请求日期,触发目标分支
设置FromDate和ToDate的差值大于365天,比如从2022年1月1日到2023年1月2日,差值为367天,确保进入if (days > 365)分支。
2. 精确验证GetResult的入参
不再用It.IsAny<Query>(),而是通过It.Is<Query>(q => ...)来验证分支执行后Query对象的FromDay和ToDay是否被正确设置为1月1日和12月31日。
3. 保留必要的返回结果验证
确保接口返回正确的状态码和数据结构。
修改后的测试代码
[Fact] public async Task Test_Run_When_Days_Greater_Than_365() { // Arrange // 设置日期差为367天,确保触发分支 var request = new Request { FromDate = new DateTimeOffset(2022, 1, 1, 0, 0, 0, TimeSpan.Zero), ToDate = new DateTimeOffset(2023, 1, 2, 0, 0, 0, TimeSpan.Zero), }; var mockValueService = new Mock<IValueService>(); var mockLogger = new Mock<ILogger<ReportsController>>(); var controller = new ReportsController(mockValueService.Object, mockLogger.Object); // 模拟返回数据,保持简洁即可 mockValueService.Setup(x => x.GetResult( It.Is<Query>(q => q.FromDay.Month == 1 && q.FromDay.Day == 1 && q.ToDay.Month == 12 && q.ToDay.Day == 31), It.IsAny<DateTimeOffset>())) .ReturnsAsync(new List<MonthLyDataModel>()); // Act var result = await controller.Run(request); // Assert // 验证返回状态码 Assert.IsType<OkObjectResult>(result); // 验证ValueService的GetResult方法被调用,且入参符合分支逻辑的预期 mockValueService.Verify(x => x.GetResult( It.Is<Query>(q => q.FromDay.Month == 1 && q.FromDay.Day == 1 && q.ToDay.Month == 12 && q.ToDay.Day == 31), It.IsAny<DateTimeOffset>()), Times.Once); }
额外优化点
- 如果需要验证非分支场景(日期差≤365天),可以新增一个测试方法,设置日期差为364天,验证
Query的FromDay和ToDay与请求日期一致。 - 对于
DateTimeOffset.UtcNow,如果需要固定时间避免测试不稳定,可以注入一个时间服务替代直接调用UtcNow,或者用Moq的回调来捕获该参数进行验证。
内容的提问来源于stack exchange,提问作者amd
相关产品推荐
相关产品推荐

