C# 实体框架6和Moq4:模拟数据库集是否可以在其范围内保留添加的数据?
背景: -使用EntityFramework 6 -使用Moq v4.2.1402.2112 -使用DbFirst方法 然而,我一直在关注EF6 Moq演练(可以找到);我一直在想,是否有可能让模拟的DbSet在其作用域期间保留添加到其中的数据 例如:C# 实体框架6和Moq4:模拟数据库集是否可以在其范围内保留添加的数据?,c#,entity-framework,moq,C#,Entity Framework,Moq,背景: -使用EntityFramework 6 -使用Moq v4.2.1402.2112 -使用DbFirst方法 然而,我一直在关注EF6 Moq演练(可以找到);我一直在想,是否有可能让模拟的DbSet在其作用域期间保留添加到其中的数据 例如: using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using System.Collections.Generic; using System.Data.Entit
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
namespace TestingDemo
{
[TestClass]
public class QueryTests
{
[TestMethod]
public void GetAllBlogs_orders_by_name()
{
var data = new List<Blog>
{
new Blog { Name = "BBB" },
new Blog { Name = "ZZZ" },
new Blog { Name = "AAA" },
}.AsQueryable();
var mockSet = new Mock<DbSet<Blog>>();
mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var mockContext = new Mock<BloggingContext>();
mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);
var service = new BlogService(mockContext.Object);
service.AddBlog("YYY", "http://blogs.msdn.com/yyyy");
var blogs = service.GetAllBlogs();
Assert.AreEqual(4, blogs.Count); // Fails because whilst the AddBlog is called, and we can verify this, the AsQueryable List doesn't retain the new data
Assert.AreEqual("AAA", blogs[0].Name);
Assert.AreEqual("BBB", blogs[1].Name);
Assert.AreEqual("YYY", blogs[2].Name);
Assert.AreEqual("ZZZ", blogs[3].Name);
}
}
}
使用Microsoft.VisualStudio.TestTools.UnitTesting;
使用最小起订量;
使用System.Collections.Generic;
使用System.Data.Entity;
使用System.Linq;
命名空间测试演示
{
[测试类]
公共类查询测试
{
[测试方法]
public void GetAllBlogs\u orders\u by\u name()
{
var数据=新列表
{
新博客{Name=“BBB”},
新博客{Name=“ZZZ”},
新博客{Name=“AAA”},
}.AsQueryable();
var mockSet=new Mock();
mockSet.As().Setup(m=>m.Provider).返回(data.Provider);
mockSet.As().Setup(m=>m.Expression).Returns(data.Expression);
mockSet.As().Setup(m=>m.ElementType).Returns(data.ElementType);
mockSet.As().Setup(m=>m.GetEnumerator()).Returns(data.GetEnumerator());
var mockContext=new Mock();
Setup(c=>c.Blogs).Returns(mockSet.Object);
var service=newblogservice(mockContext.Object);
service.AddBlog(“YYY”http://blogs.msdn.com/yyyy");
var blogs=service.GetAllBlogs();
Assert.AreEqual(4,blogs.Count);//失败,因为在调用AddBlog时,我们可以验证这一点,但AsQueryable列表不保留新数据
Assert.AreEqual(“AAA”,博客[0].Name);
Assert.AreEqual(“BBB”,blogs[1].Name);
Assert.AreEqual(“YYY”,blogs[2].Name);
Assert.AreEqual(“ZZZ”,blogs[3].Name);
}
}
}
是否可以保留添加的数据,以便稍后在测试中查询它?您需要做的是对您希望修改集合的所有方法使用回调。动态代理中的回调是在调用方法时运行的代码,并且可以访问参数 创建一个临时集合来保存数据(如示例代码中的
数据
)。然后实现回调(1):对于可以修改集合的动态代理的每个方法。在这些回调中,修改临时集合(数据
)。然后在动态代理中,模拟Count
方法以返回此集合中的计数(data
)
您的临时集合已创建:数据
(2) 添加回调的方法是您的服务用于添加blog的方法,BlogService.AddBlog
我不使用Moq4,因此我不会向您显示语法,而是使用此语法添加所需的回调
模拟具有许多方法和属性以及复杂行为(如DbSet
)的对象的问题是:
- 您需要在动态代理中实现大量回调:有多少方法可以修改集合,如(1)所述
- 或者您需要了解测试代码的实现细节,以便只实现必要的回调,如(2)Oops!那太难看了。您可以实现一些有效的方法,但是测试可能会失败,因为您没有模拟所需的方法
您可以在模拟机上尝试
CallBase=true
var mockSet=newmock{CallBase=true}代码>所以moq将调用DbSet的Add方法。感谢您的回复,但是这似乎没有帮助;我是否需要为add函数设置某种链接以添加到基础列表中?回调是我所问问题的答案,这非常适合基本单元测试;然而,关于内存中数据库实现的额外信息非常有用。我现在正在研究用于更复杂的集成测试。是的,使用内存中的DB使测试更容易:您不必为EF上下文编写mock(事实上,有一些mock很难实现,即使用几个Include()
模拟查询,或者使用相关实体更新实体:使用内存中的,这非常容易)。此外,测试的运行速度将与使用模拟的单元测试一样快,因为它们不会达到真正的DB或需要在磁盘上读/写。(我仍然更喜欢将EF与接口解耦,并模拟这些接口,因为我使用不同的体系结构,并且盲目地依赖真实的接口实现)