EF Entry.State的单元测试失败

本文关键字:单元测试 失败 State Entry EF | 更新日期: 2023-09-27 18:21:27

是否可以对其进行单元测试?

public class MyRepository<T> where T : IdentityUser, new()
{
   public async Task UpdateAsync(T user)
    {
        _context.Entry(user).State = EntityState.Modified;
        _context.Entry(user).Property("UserName").IsModified = false;
        await _context.SaveChangesAsync();
    }
}

[TestInitialize]将1个用户添加到存储库

_user = new IdentityUser { Id = "70a038cdde40" };
IDbSet<IdentityUser> users = new FakeDbSet<IdentityUser> { _user };
var dbContext = new Mock<MyDbContext<IdentityUser>>();
dbContext.Setup(x => x.Users).Returns(() => users);
_repository = new MyRepository<IdentityUser>(dbContext.Object);

我正试着用这个进行测试

private MyRepository<IdentityUser> _repository;
[TestMethod]
public async Task UpdateUser_Success2()
{
    var user = await _repository.FindByIdAsync("70a038cdde40");
    Assert.IsFalse(user.EmailConfirmed, "User.EmailConfirmed is True");
    user.EmailConfirmed = true;
    await _repository.UpdateAsync(user);
    (...)
}

但它在UpdateAsync的第一行就死了。测试是错误的还是UpdateAsync实现?有什么办法我可以测试一下吗?

编辑

我按照Belogix 的建议添加

 dbContext.Setup(x => x.Entry(It.IsAny<IdentityUser>()))
                       .Returns(() => dbContext.Object.Entry(_user));

我想,这让我更接近了,但仍然有非虚拟错误:在非虚拟成员上的无效设置:x=>x.Entry(It.IsAny())

EF Entry.State的单元测试失败

有史以来最好的一句话:"计算机科学中的所有问题都可以通过另一种间接方法来解决"-Butler Lampson。

如果不添加一些额外的抽象,就无法直接测试它。我不得不以这种方式重构UpdateAsync方法

public async Task UpdateAsync(T user)
{
    SetEntityStateModified(user);
    SetPropertyIsModified(user);
    await _context.SaveChangesAsync();
}
public virtual void SetPropertyIsModified(T user)
{
    _context.Entry(user).Property("UserName").IsModified = false;
}
public virtual void SetEntityStateModified(T user)
{
    _context.Entry(user).State = EntityState.Modified;
}

然后在初始化中更新我的测试代码

_repository = new Mock<MyRepository<IdentityUser>>(dbContext.Object);
_repository.Setup(x => x.SetEntityStateModified(It.IsAny<IdentityUser>()));
_repository.Setup(x => x.SetPropertyIsModified(It.IsAny<IdentityUser>()));

我的测试最终通过了

[TestMethod]
public async Task can_update_user_details()
{
    //Arrange
    var user = await _repository.Object.FindByIdAsync("70a038cdde40");
    Assert.IsFalse(user.EmailConfirmed, "User.EmailConfirmed is True");
    //Act            
    user.EmailConfirmed = true;
    await _repository.Object.UpdateAsync(user);
    var newUser = await _repository.Object.FindByIdAsync("70a038cdde40");
    //Assert
    Assert.IsTrue(newUser.EmailConfirmed, "User.EmailConfirmed is False");
}
dbContext中的ChangeTracker跟踪更改并保存更改的实体。因此,您可以断言更改后的实体就在其中。
Assert.IsTrue(dbContext.Object.ChangeTracker.Entries().Any(entry =>
            entry.State == EntityState.Modified &&
            entry.Entity is IdentityUser &&
            (entry.Entity as IdentityUser).Id == users[0].Id // Here you can check if it is actually the same user
        ));

对于该物业来说,它将是这样的:

Assert.IsTrue(_context.Object.ChangeTracker.Entries().Any(entry =>
            entry.Property("UserName").IsModified == false &&
            entry.Entity is IdentityUser &&
            (entry.Entity as IdentityUser).Id == users[0].Id // Here you can check if it is actually the same user
        ));

看起来您没有正确地清除context。。。我不是在使用Visual Studio的计算机上,所以这里有一些伪代码,应该可以演示我的意思。如果您想处理不同的响应,可以用您的嘲笑框架来忽略参数或实际的用户来替换IsAnything

// Existing context initialisation...
var dbContext = new Mock<MyDbContext<IdentityUser>>();
dbContext.Setup(x => x.Users).Returns(() => users);
// NEW: Mock what / how Entry is going to return when called (i.e. return a user)
dbContext.Setup(x => x.Entry(IsAnything)).Returns(() => users[0]);