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

.NET中Xunit测试无法拦截餐盒重复预约问题求助

问题:同一取餐日期重复预约餐盒的测试未触发预期错误

我开发了一个食物浪费系统,学生可低价预约餐盒并取餐。现在编写Xunit测试验证「同一取餐日期不可预约2份餐盒」的规则,但测试中餐盒仍被成功预约,未触发预期的错误提示。尝试调整餐盒添加方式无效,恳请排查问题原因。

测试代码

[Fact]
//unhappy
public void MealBox_Student_Already_Reserved_Same_Date()
{
    // Arrange
    var repositoryManagerMock = new Mock<IRepositoryManager>();

    ServiceManager _ss = new ServiceManager(repositoryManagerMock.Object);
    
    var MealboxList = new List<MealBox>();
    
    var Student = new Student
    {
        Id = 1,
        FirstName = "Test",
        LastName = "Student",
        Email = "test@gmail.com",
        DateOfBirth = DateTime.Now.AddYears(-15),
        StudentNumber = "210897",
        StudyCity = City.Breda,
        MobileNumber = "0612345678",
        ReservedMealBoxes = MealboxList
    };
    
    MealboxList.Add(new MealBox
    {
        Id = 1,
        Name = "Test MealBox",
        PickupDateTime = DateTime.Now.AddDays(1),
        LatestPickupTime = DateTime.Now.TimeOfDay,
        IndicationalIs18Plus = false,
        IsHotMeal = false,
        Price = 10,
        MealType = MealType.ColdLunch,
        ExampleProductsBasedOnHistory = new List<Product>(),
        ReservedBy = Student,
        ReservedById = 1,
        OfferedAtCanteen = null,
        OfferedAtCanteenId = 1
    });
    
    var MealBox = new MealBox
    {
        Id = 2,
        Name = "Test MealBox",
        PickupDateTime = DateTime.Now.AddDays(1),
        LatestPickupTime = DateTime.Now.TimeOfDay,
        IndicationalIs18Plus = false,
        IsHotMeal = false,
        Price = 10,
        MealType = MealType.ColdLunch,
        ExampleProductsBasedOnHistory = new List<Product>(),
        ReservedBy = null,
        ReservedById = null,
        OfferedAtCanteen = null,
        OfferedAtCanteenId = 1
    };
    
    repositoryManagerMock.Setup(x => x.MealBox.GetById(2)).Returns(MealBox);
    repositoryManagerMock.Setup(x => x.Student.GetByEmail(Student.Email)).Returns(Student);

    // Act
    ServiceResponse message = _ss.MealBox.Reserve(2, Student.Email);

    // Assert
    Assert.Equal("You have already reserved a mealbox for this date", message.Message);

}

验证器代码

public ServiceResponse Reserve(int id, string email)
{
    var response = new ServiceResponse();

    var student = _repositoryManager.Student.GetByEmail(email);
    if (student == null)
    {
        response.Success = false;
        response.Message = "Student not found";
        return response;
    }

    var mealBox = _repositoryManager.MealBox.GetById(id);
    if (mealBox == null)
    {
        response.Success = false;
        response.Message = "MealBox not found";
        return response;
    }

    var resultOfChecks = MealBoxReserveChecks(response, mealBox, student);
    if (!resultOfChecks.Success)
    {
        return resultOfChecks;
    }

    mealBox.ReservedById = student.Id;

    try
    {
        _repositoryManager.MealBox.Update(mealBox);
        _repositoryManager.Save();
    }
    catch (Exception ex)
    {
        throw ex;
    }

    response.Success = true;
    response.Message = "MealBox reserved successfully!";
    return response;
}

private ServiceResponse MealBoxReserveChecks(ServiceResponse response, MealBox mealBox, Student student)
{
    if (mealBox.ReservedBy != null)
    {
        response.Success = false;
        response.Message = "MealBox already reserved";
        return response;
    }

    if (mealBox.IndicationalIs18Plus)
    {
        int hasToBePickedUp = Int32.Parse(mealBox.PickupDateTime.ToString("yyyyMMdd"));
        int dateOfBirth = Int32.Parse(student.DateOfBirth.ToString("yyyyMMdd"));

        if (((hasToBePickedUp - dateOfBirth) / 10000) < 18)
        {
            response.Success = false;
            response.Message = "You are not 18+ and cannot reserve this mealbox";
            return response;
        }
    }

    var reservedMealBoxes = _repositoryManager.MealBox.GetAll().Where(mb => mb.ReservedById == student.Id).ToList();
    
    if (reservedMealBoxes.Any(mb => mb.PickupDateTime.Day == mealBox.PickupDateTime.Day))
    {
        response.Success = false;
        response.Message = "You have already reserved a mealbox for this date";
        return response;
    }

    response.Success = true;
    return response;
}

问题原因及修复方案

核心问题

  1. 模拟数据源缺失:验证逻辑通过_repositoryManager.MealBox.GetAll()查询学生已预约餐盒,但测试中未对该方法做模拟设置,默认返回空集合,导致检测不到已存在的同日期预约。
  2. 日期判断逻辑不严谨:仅用PickupDateTime.Day比较天数,会把不同月份的同一天(如1月1日和2月1日)误判为同一日期。

修复步骤

1. 补充GetAll()方法的模拟

在测试的Arrange部分添加以下代码,让仓储返回包含已预约餐盒的完整集合:

var allMealBoxes = new List<MealBox>
{
    MealboxList.First(), // 学生已预约的餐盒(Id=1)
    MealBox // 待预约的餐盒(Id=2)
};

repositoryManagerMock.Setup(x => x.MealBox.GetAll()).Returns(allMealBoxes);

2. 修正日期比较逻辑

将验证器中的日期判断改为比较Date属性,确保年、月、日完全匹配:

if (reservedMealBoxes.Any(mb => mb.PickupDateTime.Date == mealBox.PickupDateTime.Date))
{
    response.Success = false;
    response.Message = "You have already reserved a mealbox for this date";
    return response;
}

3. 可选:固定测试时间避免干扰

测试中使用DateTime.Now可能因执行时差导致日期不一致,可提前定义固定日期:

var pickupDate = DateTime.Now.AddDays(1).Date;

// 已预约餐盒的PickupDateTime
PickupDateTime = pickupDate,
// 待预约餐盒的PickupDateTime
PickupDateTime = pickupDate,

内容的提问来源于stack exchange,提问作者Joep Donkers

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 00:40:39