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
相关产品推荐
相关产品推荐

