模拟DbSet方法在控制器内部调用时抛出NotImplementedException

本文关键字:NotImplementedException 调用 内部 DbSet 方法 控制器 模拟 | 更新日期: 2023-09-27 18:06:43

我有以下设置:

DbContext:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public virtual DbSet<Album> Album { get; set; }
    public ApplicationDbContext()
        : base("DefaultConnection", throwIfV1Schema: false)
    {
    }
    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }
}

模型:

public class Album
{
    public int AlbumID { get; set; }
    [StringLength(150)]
    public string Title { get; set; }
}

控制器:

public class AlbumController : Controller
{
    ApplicationDbContext db = new ApplicationDbContext();
    public AlbumController(ApplicationDbContext injectDb)
    {
        db = injectDb;
    }
    // POST: Albums/Delete/5
    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    [Authorize(Roles = "Admin")]
    public ActionResult DeleteConfirmed(int id)
    {
        Album album = db.Album.Find(id);
        db.Album.Remove(album);
        db.SaveChanges();
        return RedirectToAction("Index");
    }
}

我使用Moq和xUnit编写了单元测试来检查deletecconfirmed功能:

 public class AlbumsControllerTests
    {
        public static Mock<DbSet<T>> MockDbSet<T>(List<T> inputDbSetContent) where T : class
        {
            var DbSetContent = inputDbSetContent.AsQueryable();
            var dbSet = new Mock<DbSet<T>>();
            dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(DbSetContent.Provider);
            dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(DbSetContent.Expression);
            dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(DbSetContent.ElementType);
            dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => inputDbSetContent.GetEnumerator());
            dbSet.Setup(m => m.Add(It.IsAny<T>())).Callback<T>((s) => inputDbSetContent.Add(s));
            dbSet.Setup(m => m.Remove(It.IsAny<T>())).Callback<T>((s) => inputDbSetContent.Remove(s));
            return dbSet;
        }
        [Fact]
        public void DeleteConfirmedTest()
        {
            // Arrange
            var mockAlbumSet = MockDbSet(new List<Album> { });
            Mock<ApplicationDbContext> sutDbContext = new Mock<ApplicationDbContext>() { CallBase = true };
            sutDbContext.Setup(m => m.Album).Returns(mockAlbumSet.Object);
            // Check if Album.Remove works inside this test
            var albumToBeDeleted = new Album() { AlbumID = 1, Title = "TestAlbumName" };
            sutDbContext.Object.Album.Add(albumToBeDeleted);
            Assert.Equal(1, (from a in sutDbContext.Object.Album select a).Count());
            sutDbContext.Object.Album.Remove(albumToBeDeleted);
            Assert.Equal(0, (from a in sutDbContext.Object.Album select a).Count());
            // Actual Test
            sutDbContext.Object.Album.Add(albumToBeDeleted);
            sutDbContext.Setup(m => m.Album.Find(It.IsAny<int>()))
            .Returns(albumToBeDeleted);
            AlbumController sut = new AlbumController(sutDbContext.Object);
            var output = sut.DeleteConfirmed(1); // Throws NotImplementedException
            // Assert
            Assert.Equal(0, (from a in sutDbContext.Object.Album select a).Count());
        }
    }

测试在db.Album.Remove(album) in deletecconfirmed :

上抛出以下异常

系统。NotImplementedException:成员'Remove'还没有被删除在类型'DbSet 1Proxy' which inherits from 'DbSet '上实现。测试'DbSet ' 1'的double必须提供方法和的实现使用的属性

正如您可以在MockDbSet方法体中看到的,我为Mock和设置了Remove方法,它在单元测试中工作得很好。你能解释一下为什么它不能在控制器里工作吗?

模拟DbSet方法在控制器内部调用时抛出NotImplementedException

你的测试将工作良好,如果你改变你的行:

sutDbContext.Setup(m => m.Album.Find(It.IsAny<int>()))
            .Returns(albumToBeDeleted);

:

mockAlbumSet.Setup(x=>x.Find(It.IsAny<int>()))
           .Returns(albumToBeDeleted);

您为sutDbContext做了一个设置,以便在调用sutDbContext.Album时返回mockAlbumSet.Object,但是那一行覆盖了您的设置,为sutDbContext.Album属性创建了一个新的模拟对象,并为该模拟创建了一个单独的设置:

m.Album.Find(It.IsAny<int>()))
            .Returns(albumToBeDeleted);

下面是一个简单的测试,它向您展示了调用类的嵌套属性的setup,该设置先前被设置为返回Mock。对象,将使用新的Mock覆盖该属性。对象:

public interface IParentService
{
    IDependantService Dependant { get; }
}
public interface IDependantService
{
    void Execute();
}
[Fact]
//This test passes
public void VerifyThatNestedMockSetupGeneratesNewMockObject()
{
    var value = 0;  
    var parentServiceMock = new Mock<IParentService>();
    var dependantServiceMock = new Mock<IDependantService>();
    dependantServiceMock.Setup(x => x.Execute()).Callback(() => { value = 1; });
    parentServiceMock.Setup(x => x.Dependant).Returns(dependantServiceMock.Object);
    Assert.Same(parentServiceMock.Object.Dependant, dependantServiceMock.Object);
    parentServiceMock.Setup(x => x.Dependant.Execute()).Callback(() => { value = 2; });
    Assert.NotSame(parentServiceMock.Object.Dependant, dependantServiceMock.Object);
    parentServiceMock.Object.Dependant.Execute();
    Assert.Equal(2, value);
}
相关文章:
  • 没有找到相关文章