模拟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方法,它在单元测试中工作得很好。你能解释一下为什么它不能在控制器里工作吗?
你的测试将工作良好,如果你改变你的行:
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);
}