嘲笑 DbSet.AddOrUpdate
Mocking DbSet.AddOrUpdate
我正在按照说明 here 尝试模拟我的 DbSet
和 DbContext
以使用 Moq 进行单元测试。
我正在测试的服务如下所示
public class MyItemService
{
private MyContext context;
public void AddItem(MyItem item)
{
this.context.MyItems.AddOrUpdate(item);
this.context.SaveChanges();
}
}
我的单元测试是这样的
[TestMethod]
public void AddItem_ShouldSucceed()
{
var myItems = new Mock<DbSet<MyItem>>();
var context = new Mock<MyContext>();
context.Setup(e => e.MyItems).Returns(myItems.Object);
MyItemService service = new MyItemService(context.Object);
service.AddItem(new MyItem
{
Id = "1234"
});
}
当我 运行 测试时,出现异常
System.InvalidOperationException: Unable to call public, instance method AddOrUpdate on derived IDbSet<T> type 'Castle.Proxies.DbSet``1Proxy'. Method not found.
我认为问题是因为 AddOrUpdate
是 DbSet
上的扩展方法。我 do System.Data.Entity.Migrations
包含在我的测试 .cs 文件中。
我尝试添加行
myItems.Setup(e => e.AddOrUpdate(It.IsAny<MyItem>()));
我的单元测试,但后来我得到了异常
System.NotSupportedException:表达式引用了一个不属于模拟对象的方法:e => e.AddOrUpdate(new[] { It.IsAny() })
当我正在测试的方法使用 AddOrUpdate
时,有什么方法可以让我的单元测试工作吗?
正如 Nkosi 在评论中所说,您在 AddOrUpdate 周围添加了包装器。
您可以像这样实现包装器:
public class MyDbContext: DbContext
{
public virtual DbSet<MyItem> Items {get; set;}
public virtual AddOrUpdate<T>(T item)
{
if(typeof(T) == typeof(MyItem))
{
Items.AddOrUpdate(item as MyItem)
}
}
}
然后从您的 class 调用它,例如:
public class MyItemService
{
private MyContext context;
public void AddItem(MyItem item)
{
this.context.AddOrUpdate(item);
this.context.SaveChanges();
}
}
我在搜索类似问题后遇到了这个老问题。
我们需要做的就是创建 MockableDbSetWithExtensions 抽象 class,并使用它代替 DbSet,每当你想测试调用 AddOrUpdate 的方法时。
It is how Entity Framework team tests their code too.
public abstract class MockableDbSetWithExtensions<T> : DbSet<T>
where T : class
{
public abstract void AddOrUpdate(params T[] entities);
public abstract void AddOrUpdate(Expression<Func<T, object>>
identifierExpression, params T[] entities);
}
用法
[TestMethod]
public void AddItem_ShouldSucceed()
{
var myItems = new Mock<MockableDbSetWithExtensions<MyItem>>();
var context = new Mock<MyContext>();
context.Setup(e => e.MyItems).Returns(myItems.Object);
MyItemService service = new MyItemService(context.Object);
...
}
我正在按照说明 here 尝试模拟我的 DbSet
和 DbContext
以使用 Moq 进行单元测试。
我正在测试的服务如下所示
public class MyItemService
{
private MyContext context;
public void AddItem(MyItem item)
{
this.context.MyItems.AddOrUpdate(item);
this.context.SaveChanges();
}
}
我的单元测试是这样的
[TestMethod]
public void AddItem_ShouldSucceed()
{
var myItems = new Mock<DbSet<MyItem>>();
var context = new Mock<MyContext>();
context.Setup(e => e.MyItems).Returns(myItems.Object);
MyItemService service = new MyItemService(context.Object);
service.AddItem(new MyItem
{
Id = "1234"
});
}
当我 运行 测试时,出现异常
System.InvalidOperationException: Unable to call public, instance method AddOrUpdate on derived IDbSet<T> type 'Castle.Proxies.DbSet``1Proxy'. Method not found.
我认为问题是因为 AddOrUpdate
是 DbSet
上的扩展方法。我 do System.Data.Entity.Migrations
包含在我的测试 .cs 文件中。
我尝试添加行
myItems.Setup(e => e.AddOrUpdate(It.IsAny<MyItem>()));
我的单元测试,但后来我得到了异常
System.NotSupportedException:表达式引用了一个不属于模拟对象的方法:e => e.AddOrUpdate(new[] { It.IsAny() })
当我正在测试的方法使用 AddOrUpdate
时,有什么方法可以让我的单元测试工作吗?
正如 Nkosi 在评论中所说,您在 AddOrUpdate 周围添加了包装器。 您可以像这样实现包装器:
public class MyDbContext: DbContext
{
public virtual DbSet<MyItem> Items {get; set;}
public virtual AddOrUpdate<T>(T item)
{
if(typeof(T) == typeof(MyItem))
{
Items.AddOrUpdate(item as MyItem)
}
}
}
然后从您的 class 调用它,例如:
public class MyItemService
{
private MyContext context;
public void AddItem(MyItem item)
{
this.context.AddOrUpdate(item);
this.context.SaveChanges();
}
}
我在搜索类似问题后遇到了这个老问题。
我们需要做的就是创建 MockableDbSetWithExtensions 抽象 class,并使用它代替 DbSet,每当你想测试调用 AddOrUpdate 的方法时。
It is how Entity Framework team tests their code too.
public abstract class MockableDbSetWithExtensions<T> : DbSet<T>
where T : class
{
public abstract void AddOrUpdate(params T[] entities);
public abstract void AddOrUpdate(Expression<Func<T, object>>
identifierExpression, params T[] entities);
}
用法
[TestMethod]
public void AddItem_ShouldSucceed()
{
var myItems = new Mock<MockableDbSetWithExtensions<MyItem>>();
var context = new Mock<MyContext>();
context.Setup(e => e.MyItems).Returns(myItems.Object);
MyItemService service = new MyItemService(context.Object);
...
}