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

C#单元测试Mock报错:ArgumentNullException: Value cannot be null. (Parameter 'source')

错误原因分析

从异常栈可知,ArgumentNullException是在调用.First()时触发的——这说明_db.LoadData(...)返回了null。核心问题是Moq的Setup参数不匹配:
在SqlData.GetRoomTypesDetailById方法中,调用LoadData时传入的第二个参数是new { RoomTypeId = roomTypeId }(匿名对象),但测试代码的Setup里传入的第二个参数是roomTypeId(int类型),参数类型完全不匹配,导致Moq没有命中预设逻辑,返回了默认值null,后续调用.First()自然抛出空引用异常。

另外测试代码里存在一个笔误:调用了不存在的GetAvailableRoomTypes()方法,而你的MockData类中只有GetRoomTypesDetailById方法。

解决方案

1. 修正Moq Setup的参数匹配

针对匿名对象的参数匹配,有两种方式可选:

方式一:宽松匹配(不关心具体参数值)

用It.IsAny<dynamic>()匹配任意动态类型参数:

mock.Mock<ISqlDataAccess>()
    .Setup(x => x.LoadData<RoomTypesModel, dynamic>(sqlStatment, It.IsAny<dynamic>(), connectionStringName, true))
    .Returns(roomList);

方式二:精确匹配(更严谨)

通过It.Is<dynamic>()断言匿名对象的属性值:

mock.Mock<ISqlDataAccess>()
    .Setup(x => x.LoadData<RoomTypesModel, dynamic>(sqlStatment, It.Is<dynamic>(p => p.RoomTypeId == roomTypeId), connectionStringName, true))
    .Returns(roomList);

2. 修正测试中获取模拟数据的代码

把不存在的GetAvailableRoomTypes()替换为MockData类中实际存在的方法:

var returnedRoom = GetRoomTypesDetailById(roomTypeId).First();

3. 修正断言逻辑(可选但必要)

默认情况下引用类型的Assert.Equal比较的是引用地址,而非属性值。需要手动比较属性,或者给RoomTypesModel重写Equals和GetHashCode方法:

// 手动比较属性的示例
Assert.NotNull(actual);
Assert.Equal(expected.Id, actual.Id);
Assert.Equal(expected.Title, actual.Title);
Assert.Equal(expected.Description, actual.Description);
Assert.Equal(expected.Price, actual.Price);
修正后的完整测试代码
public class SqlDataTests : MockData
{
    private static DateTime pStartDate = DateTime.Now;
    private DateTime pEndDate = pStartDate.AddDays(1);

    private const string connectionStringName = "SqlDb";
    
    [Fact]
    public void GetRoomTypesDetailById_ShouldReturnRoomDetails()
    {
        var roomTypeId = 2;
        var returnedRoom = GetRoomTypesDetailById(roomTypeId).First();
        var sqlStatment = "[dbo].[spRoomTypeDetails_GetById]";
        List<RoomTypesModel> roomList = new List<RoomTypesModel>();
        roomList.Add(returnedRoom);

        using (var mock = AutoMock.GetLoose())
        {
            mock.Mock<ISqlDataAccess>()
                    .Setup(x => x.LoadData<RoomTypesModel, dynamic>(sqlStatment, It.Is<dynamic>(p => p.RoomTypeId == roomTypeId), connectionStringName, true))
                    .Returns(roomList);

            var _dbMock = mock.Create<SqlData>();

            var expected = returnedRoom;
            var actual = _dbMock.GetRoomTypesDetailById(roomTypeId);

            Assert.NotNull(actual);
            Assert.Equal(expected.Id, actual.Id);
            Assert.Equal(expected.Title, actual.Title);
            Assert.Equal(expected.Description, actual.Description);
            Assert.Equal(expected.Price, actual.Price);
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 01:45:37