You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 16:55:22