dbContext 的 EF Mock 不要使用预加载
EF Mock of dbContext dont use eager loading
我尝试使用 NUnit 和 Moq 对访问数据的服务进行单元测试。我像这样创建 DbContext 的模拟:
我的实体和上下文
public class BloggingContext : DbContext
{
public virtual DbSet<Blog> Blogs { get; set; }
public virtual DbSet<Post> Posts { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public virtual List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; }
}
测试方法:
public void CreateBlog_saves_a_blog_via_context()
{
var data = new List<Blog>
{
new Blog { Name = "BBB" },
new Blog { Name = "ZZZ" },
new Blog { Name = "AAA" },
}.AsQueryable();
var data2 = new List<Blog>
{
new Post{ Title= "BBB" },
new Post{ Title= "ZZZ" },
new Post{ Title= "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 mockSet2 = new Mock<DbSet<POs>>();
mockSet2.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data2.Provider);
mockSet2.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data2.Expression);
mockSet2.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data2.ElementType);
mockSet2.As<IQueryable<Post>>().Setup(m => m.GetEnumerator()).Returns(data2.GetEnumerator());
var mockContext = new Mock<BloggingContext>();
mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);
mockContext.Setup(c => c.Posts).Returns(mockSet2.Object);
var service = new BlogService(mockContext.Object);
var blogs = service.GetAllBlogs();
}
但是当我不想设置实体之间的关系时,我会测试它们
Post = new Post(){
Title ="",
Content = "",
BlogID = 2
};
但是导航 属性 没有在测试中设置(在正常设置中它的设置)。当我尝试从上下文中读取此 post 时,我始终为空,但 BlogID 已设置。
我会以不同的方式对此进行单元测试,查看单元测试的名称,您只是想确保在创建博客时将正确的数据传递到 DbContext 中。您可以这样做跟随;
[TestMethod]
public void CreateBlog_saves_a_blog_via_context()
{
var mockContext = new Mock<BloggingContext>();
// Add related posts directly into fake blog data
var data = new List<Blog>
{
new Blog {Name = "BBB", Posts = new List<Post>() { new Post() { Title = "Post1"} } },
new Blog {Name = "ZZZ", Posts = new List<Post>() { new Post() { Title = "Post2"} } },
new Blog {Name = "AAA", Posts = new List<Post>() { new Post() { Title = "Post3"} } },
}.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());
mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);
var service = new BlogService(mockContext.Object);
var newBlog = new Blog {Name = "A New Blog", Posts = new List<Post>() {new Post() {Title = "Post1"}}};
// Add new Blog to service
service.AddBlog(newBlog);
// Check that DbContext received the new blog correctly
// And Blog contained correct name and Post count of 1
mockSet.Verify(x=>x.Add(It.Is<Blog>(blog=>blog.Name == "A New Blog" && blog.Posts.Count == 1)));
}
正如我希望您看到的那样,您确保被测试的 class 正确地将正确的对象传递给 DbContext。您现在还可以进行其他断言,例如确认 DbContext 中的 SaveChanges() 方法也仅被调用一次。
测试您的 class,而不是 DbContext
我尝试使用 NUnit 和 Moq 对访问数据的服务进行单元测试。我像这样创建 DbContext 的模拟:
我的实体和上下文
public class BloggingContext : DbContext
{
public virtual DbSet<Blog> Blogs { get; set; }
public virtual DbSet<Post> Posts { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public virtual List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; }
}
测试方法:
public void CreateBlog_saves_a_blog_via_context()
{
var data = new List<Blog>
{
new Blog { Name = "BBB" },
new Blog { Name = "ZZZ" },
new Blog { Name = "AAA" },
}.AsQueryable();
var data2 = new List<Blog>
{
new Post{ Title= "BBB" },
new Post{ Title= "ZZZ" },
new Post{ Title= "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 mockSet2 = new Mock<DbSet<POs>>();
mockSet2.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data2.Provider);
mockSet2.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data2.Expression);
mockSet2.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data2.ElementType);
mockSet2.As<IQueryable<Post>>().Setup(m => m.GetEnumerator()).Returns(data2.GetEnumerator());
var mockContext = new Mock<BloggingContext>();
mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);
mockContext.Setup(c => c.Posts).Returns(mockSet2.Object);
var service = new BlogService(mockContext.Object);
var blogs = service.GetAllBlogs();
}
但是当我不想设置实体之间的关系时,我会测试它们
Post = new Post(){
Title ="",
Content = "",
BlogID = 2
};
但是导航 属性 没有在测试中设置(在正常设置中它的设置)。当我尝试从上下文中读取此 post 时,我始终为空,但 BlogID 已设置。
我会以不同的方式对此进行单元测试,查看单元测试的名称,您只是想确保在创建博客时将正确的数据传递到 DbContext 中。您可以这样做跟随;
[TestMethod]
public void CreateBlog_saves_a_blog_via_context()
{
var mockContext = new Mock<BloggingContext>();
// Add related posts directly into fake blog data
var data = new List<Blog>
{
new Blog {Name = "BBB", Posts = new List<Post>() { new Post() { Title = "Post1"} } },
new Blog {Name = "ZZZ", Posts = new List<Post>() { new Post() { Title = "Post2"} } },
new Blog {Name = "AAA", Posts = new List<Post>() { new Post() { Title = "Post3"} } },
}.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());
mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);
var service = new BlogService(mockContext.Object);
var newBlog = new Blog {Name = "A New Blog", Posts = new List<Post>() {new Post() {Title = "Post1"}}};
// Add new Blog to service
service.AddBlog(newBlog);
// Check that DbContext received the new blog correctly
// And Blog contained correct name and Post count of 1
mockSet.Verify(x=>x.Add(It.Is<Blog>(blog=>blog.Name == "A New Blog" && blog.Posts.Count == 1)));
}
正如我希望您看到的那样,您确保被测试的 class 正确地将正确的对象传递给 DbContext。您现在还可以进行其他断言,例如确认 DbContext 中的 SaveChanges() 方法也仅被调用一次。
测试您的 class,而不是 DbContext