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

使用xUnit测试EF中Attach方法时触发NullReferenceException问题排查

为更新服务编写xUnit测试时遇到空引用异常

问题说明

为更新服务编写xUnit测试时触发空引用异常,报错行指向_dbContext.Entry(user).Property(x => x.Name).IsModified = true;,不清楚如何正确Mock相关对象解决该问题。

Update方法代码

public bool Update(string Id, CreateUpdateUserDto InP)
{
    var user = new UserDetails
    {
        Id = Id,
        Name = InP.Name
    };
    _dbContext.userDetails.Attach(user);
    _dbContext.Entry(user).Property(x => x.Name).IsModified = true;
    return _dbContext.SaveChanges() > 0;
}

测试用例代码

private readonly Mock<UserDbContext> _mockDB;
private UserRepository _repo;

public UserRepositoryTests()
{
    var data = new List<UserDetails>
    {
        new UserDetails{Id="GuId1",Name="Test1"},
        new UserDetails{Id="GuId2",Name="Test2"},
        new UserDetails{Id="GuId3",Name="Test3"}
    }.AsQueryable();
    var mockSet = new Mock<DbSet<UserDetails>>();
    mockSet.As<IQueryable<UserDetails>>().Setup(m => m.Provider).Returns(data.Provider);
    mockSet.As<IQueryable<UserDetails>>().Setup(m => m.Expression).Returns(data.Expression);
    mockSet.As<IQueryable<UserDetails>>().Setup(m => m.ElementType).Returns(data.ElementType);
    mockSet.As<IQueryable<UserDetails>>().Setup(x => x.GetEnumerator()).Returns(() => data.GetEnumerator());

    _mockDB = new Mock<UserDbContext>();
    _mockDB.Setup(y=>y.userDetails).Returns(mockSet.Object);

    _repo = new UserRepository(_mockDB.Object);
}

[Fact]
public void UpdateSuccess()
{
    var InP = new CreateUpdateUserDto()
    {
        Name = "Test"
    };
    var result = _repo.Update("GuId1", InP);
    Assert.IsType<bool>(result);
    Assert.True(result);
}

报错信息

System.NullReferenceException: Object reference not set to an instance of an object.
Stack Trace: 

UserRepository.Update(String Id, CreateUpdateUserDto InP) line 35
UserRepositoryTests.UpdateSuccess() line 71


解决方案

空引用异常是因为仅Mock了DbSet<UserDetails>,但未MockDbContext.Entry<TEntity>方法的返回值,调用_dbContext.Entry(user)时返回null,导致后续访问Property抛出异常。需补充以下Mock配置:

1. 完善测试初始化逻辑

在测试类的构造函数中,_mockDB.Setup(y=>y.userDetails).Returns(mockSet.Object);之后添加以下代码:

// Mock DbContext.Entry方法,返回模拟的EntityEntry<UserDetails>实例
var mockEntityEntry = new Mock<EntityEntry<UserDetails>>();
// Mock EntityEntry.Property方法,返回模拟的PropertyEntry实例
var mockPropertyEntry = new Mock<PropertyEntry>();
mockEntityEntry.Setup(e => e.Property(It.IsAny<Expression<Func<UserDetails, object>>>())).Returns(mockPropertyEntry.Object);
// 配置DbContext.Entry返回上述模拟实例
_mockDB.Setup(c => c.Entry(It.IsAny<UserDetails>())).Returns(mockEntityEntry.Object);
// Mock SaveChanges方法,返回1表示更新操作成功
_mockDB.Setup(c => c.SaveChanges()).Returns(1);

2. 验证修改效果

修改后,_dbContext.Entry(user)会返回模拟的EntityEntry实例,调用Property可拿到模拟的PropertyEntry,设置IsModified不会触发空引用;同时SaveChanges返回1,测试断言会顺利通过。

若需验证IsModified是否被正确设置,可在测试方法末尾添加验证代码:

mockPropertyEntry.VerifySet(p => p.IsModified = true, Times.Once);

完整修改后的测试初始化代码

public UserRepositoryTests()
{
    var data = new List<UserDetails>
    {
        new UserDetails{Id="GuId1",Name="Test1"},
        new UserDetails{Id="GuId2",Name="Test2"},
        new UserDetails{Id="GuId3",Name="Test3"}
    }.AsQueryable();
    var mockSet = new Mock<DbSet<UserDetails>>();
    mockSet.As<IQueryable<UserDetails>>().Setup(m => m.Provider).Returns(data.Provider);
    mockSet.As<IQueryable<UserDetails>>().Setup(m => m.Expression).Returns(data.Expression);
    mockSet.As<IQueryable<UserDetails>>().Setup(m => m.ElementType).Returns(data.ElementType);
    mockSet.As<IQueryable<UserDetails>>().Setup(x => x.GetEnumerator()).Returns(() => data.GetEnumerator());

    _mockDB = new Mock<UserDbContext>();
    _mockDB.Setup(y=>y.userDetails).Returns(mockSet.Object);

    // 新增的Mock配置
    var mockEntityEntry = new Mock<EntityEntry<UserDetails>>();
    var mockPropertyEntry = new Mock<PropertyEntry>();
    mockEntityEntry.Setup(e => e.Property(It.IsAny<Expression<Func<UserDetails, object>>>())).Returns(mockPropertyEntry.Object);
    _mockDB.Setup(c => c.Entry(It.IsAny<UserDetails>())).Returns(mockEntityEntry.Object);
    _mockDB.Setup(c => c.SaveChanges()).Returns(1);

    _repo = new UserRepository(_mockDB.Object);
}

补充说明

Update方法中直接创建UserDetails实例并Attach的逻辑在真实DbContext中可行,但Mock场景下需确保所有被调用的DbContext成员都被正确模拟。后续若需修改其他属性,只需扩展mockEntityEntry的Setup配置即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 12:52:39