文档章节

EntityFramework测试

z
 zhv
发布于 2017/05/25 00:27
字数 1256
阅读 19
收藏 0

这个是从MSDN上搞的。试用了一下,感觉如果老代码和示例不同的话,就不那么好用了。还是得自己重写一些东西。这方法有用,但是用处不是很大。

简述

可以自己建立一个测试替身(test double):就是自己写一个在内存中的Context和DbSets 也可以用一个mock框架来建立一个:比如用Moq 用这种内存方法在单元测试的时候可能比较好。但是使用LinQ to Objects来操作内存数据和LinQ to Entity来操作数据库还是有区别的。

建立一个单元测试

被测的EF 模型

using System.Collections.Generic; 
using System.Data.Entity; 
 
namespace TestingDemo 
{ 
    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; } 
    } 
}

被测方法

下面的测试是

  • 建立一个新Blog(AddBlog)
  • 返回所有Blogs,返回数据用名称排序(GetAllBlogs)。
  • 另外再提供一个异步方法,获取所有的以名称排序的blogs(GetAllBlogsAsync)
using System.Collections.Generic; 
using System.Data.Entity; 
using System.Linq; 
using System.Threading.Tasks; 
 
namespace TestingDemo 
{ 
    public class BlogService 
    { 
        private BloggingContext _context; 
 
        public BlogService(BloggingContext context) 
        { 
            _context = context; 
        } 
 
        public Blog AddBlog(string name, string url) 
        { 
            var blog = _context.Blogs.Add(new Blog { Name = name, Url = url }); 
            _context.SaveChanges(); 
 
            return blog; 
        } 
 
        public List<Blog> GetAllBlogs() 
        { 
            var query = from b in _context.Blogs 
                        orderby b.Name 
                        select b; 
 
            return query.ToList(); 
        } 
 
        public async Task<List<Blog>> GetAllBlogsAsync() 
        { 
            var query = from b in _context.Blogs 
                        orderby b.Name 
                        select b; 
 
            return await query.ToListAsync(); 
        } 
    } 
}

测试一个非查询场景

下面是对非查询语句进行测试

  • 使用了Moq建立了一个Context,然后它建立了一个DbSet<Blog>,他们一起返回一个上下文(Context)的Blogs属性。
  • 然后利用BlogService建立一个导向Moq的Blog。
  • 然后使用AddBlog方法,建立一个Blog
  • 最后测试验证,service是否新增了一个blog,并且在Context上使用了SaveChanges。
using Microsoft.VisualStudio.TestTools.UnitTesting; 
using Moq; 
using System.Data.Entity; 
 
namespace TestingDemo 
{ 
    [TestClass] 
    public class NonQueryTests 
    { 
        [TestMethod] 
        public void CreateBlog_saves_a_blog_via_context() 
        { 
            var mockSet = new Mock<DbSet<Blog>>(); 
 
            var mockContext = new Mock<BloggingContext>(); 
            mockContext.Setup(m => m.Blogs).Returns(mockSet.Object); 
 
            var service = new BlogService(mockContext.Object); 
            service.AddBlog("ADO.NET Blog", "http://blogs.msdn.com/adonet"); 
 
            mockSet.Verify(m => m.Add(It.IsAny<Blog>()), Times.Once()); 
            //mockSet.Verify(m => m.Add(It.Is<Blog>(n => n.customEqual(new Blog { Name = "ADO.NET Blog", Url = "http://blogs.msdn.com/adonet" })), Times.Once());
            mockContext.Verify(m => m.SaveChanges(), Times.Once()); 
        } 
    } 
}

测试查询语句

为了可以在模拟的DbSet上执行查询语句,需要一个IQuerable的实现。

  • 第一步是建立一些内存数据,使用List<Blog>.AsQueryable()
  • 第二步建立一个Context和DbSet<Blog>
  • 然后把IQueryable实现给Dbset设置上,就是委托LINQ to Objects来操作List<T>

然后就可以在虚拟的mock上建立一个BlogService,然后用GetAllBlogs获取所有的用名称排列的data

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); 
            var blogs = service.GetAllBlogs(); 
 
            Assert.AreEqual(3, blogs.Count); 
            Assert.AreEqual("AAA", blogs[0].Name); 
            Assert.AreEqual("BBB", blogs[1].Name); 
            Assert.AreEqual("ZZZ", blogs[2].Name); 
        } 
    } 
}

测试异步查询

为了使用异步查询语句,需要u多做一些工作,如果直接使用getAllBlogsAsync 来查询Moq的DbSet,就会出现以下错误

System.InvalidOperationException: The source IQueryable doesn't implement IDbAsyncEnumerable<TestingDemo.Blog>. Only sources that implement IDbAsyncEnumerable can be used for Entity Framework asynchronous operations. For more details see http://go.microsoft.com/fwlink/?LinkId=287068.

为了使用异步方法,需要建立一个内存中的 DbAsyncQueryProvider 来执行异步查询语句。 同时这样就可以设置一个用Moq执行的查询语句。

using System.Collections.Generic; 
using System.Data.Entity.Infrastructure; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Threading; 
using System.Threading.Tasks; 
 
namespace TestingDemo 
{ 
    internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider 
    { 
        private readonly IQueryProvider _inner; 
 
        internal TestDbAsyncQueryProvider(IQueryProvider inner) 
        { 
            _inner = inner; 
        } 
 
        public IQueryable CreateQuery(Expression expression) 
        { 
            return new TestDbAsyncEnumerable<TEntity>(expression); 
        } 
 
        public IQueryable<TElement> CreateQuery<TElement>(Expression expression) 
        { 
            return new TestDbAsyncEnumerable<TElement>(expression); 
        } 
 
        public object Execute(Expression expression) 
        { 
            return _inner.Execute(expression); 
        } 
 
        public TResult Execute<TResult>(Expression expression) 
        { 
            return _inner.Execute<TResult>(expression); 
        } 
 
        public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken) 
        { 
            return Task.FromResult(Execute(expression)); 
        } 
 
        public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken) 
        { 
            return Task.FromResult(Execute<TResult>(expression)); 
        } 
    } 
 
    internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T> 
    { 
        public TestDbAsyncEnumerable(IEnumerable<T> enumerable) 
            : base(enumerable) 
        { } 
 
        public TestDbAsyncEnumerable(Expression expression) 
            : base(expression) 
        { } 
 
        public IDbAsyncEnumerator<T> GetAsyncEnumerator() 
        { 
            return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator()); 
        } 
 
        IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() 
        { 
            return GetAsyncEnumerator(); 
        } 
 
        IQueryProvider IQueryable.Provider 
        { 
            get { return new TestDbAsyncQueryProvider<T>(this); } 
        } 
    } 
 
    internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T> 
    { 
        private readonly IEnumerator<T> _inner; 
 
        public TestDbAsyncEnumerator(IEnumerator<T> inner) 
        { 
            _inner = inner; 
        } 
 
        public void Dispose() 
        { 
            _inner.Dispose(); 
        } 
 
        public Task<bool> MoveNextAsync(CancellationToken cancellationToken) 
        { 
            return Task.FromResult(_inner.MoveNext()); 
        } 
 
        public T Current 
        { 
            get { return _inner.Current; } 
        } 
 
        object IDbAsyncEnumerator.Current 
        { 
            get { return Current; } 
        } 
    } 
}

以上就是我们的异步查询方法,这样就可以写一个单元测试来测试GetAllBlogsAsync方法。

using Microsoft.VisualStudio.TestTools.UnitTesting; 
using Moq; 
using System.Collections.Generic; 
using System.Data.Entity; 
using System.Data.Entity.Infrastructure; 
using System.Linq; 
using System.Threading.Tasks; 
 
namespace TestingDemo 
{ 
    [TestClass] 
    public class AsyncQueryTests 
    { 
        [TestMethod] 
        public async Task GetAllBlogsAsync_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<IDbAsyncEnumerable<Blog>>() 
                .Setup(m => m.GetAsyncEnumerator()) 
                .Returns(new TestDbAsyncEnumerator<Blog>(data.GetEnumerator())); 
 
            mockSet.As<IQueryable<Blog>>() 
                .Setup(m => m.Provider) 
                .Returns(new TestDbAsyncQueryProvider<Blog>(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); 
            var blogs = await service.GetAllBlogsAsync(); 
 
            Assert.AreEqual(3, blogs.Count); 
            Assert.AreEqual("AAA", blogs[0].Name); 
            Assert.AreEqual("BBB", blogs[1].Name); 
            Assert.AreEqual("ZZZ", blogs[2].Name); 
        } 
    } 
}

© 著作权归作者所有

z

zhv

粉丝 0
博文 22
码字总数 18361
作品 0
西安
QA/测试工程师
私信 提问
请确保在应用程序配置文件的“entityFramework”节中注册了该提供程序

异常原因 在项目中,我把数据库模型成单独分在一个 Models 类库中, 并添加了 EntityFramework. 但是在要发布的 Web 类库中, 并没有添加 EntityFramework 相关的依赖和配置. 网站发布到IIS后,...

taadis
0005/02/12
0
0
别跟我谈EF抵抗并发,敢问你到底会不会用EntityFramework

前言 一直以来写的博文都是比较温婉型的博文,今天这篇博文算是一篇批判性博文,有问题欢迎探讨,如标题,你到底会不会用EntityFramework啊。 你到底会不会用EntityFramework啊   面试过三...

dotNET跨平台
2018/05/07
0
0
EntityFramework 6 分页模式

在我的另一篇博客中提到了EntityFrameworkCore 分页问题,中提到了在针对不同版本SQL Server数据库时如何指定分页模式,那么如何在中指定分页模式呢? 场景重现 在一个项目开发的项目中,我引用了...

taadis
2017/12/27
0
0
Asp.Net MVC5 响应式论坛

响应式论坛开发与源码提供。 主要开发技术为:EntityFramework、MySql、Asp.net Mvc5.2.3、Unity IOC MySql调试通过,默认支持所有支持EntityFramework的关系型数据库 示例:http://f.yjx.in...

江上烟波
2016/10/17
2
0
MVC4建立DBContext的EF6数据

MVC4建立DBContext的EF6数据时 1.需要using System.Data.Entity;命名空间 2.此命名空间需要安装EntityFromwork.dll,此dll可以在项目——》管理NuGet程序包里联机搜索EntityFromwork并安装即...

随缘不变
2018/06/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

苹果面向Mac发布英特尔处理器漏洞缓解措施

去年苹果向Safari浏览器推出安全更新以修复基于ARM价格和英特尔的处理器存在的推测执行系列安全漏洞。 不过当时苹果并未发布有关处理器性能下降的测试结果,但大家都知道安装缓解措施会造成处...

linux-tao
41分钟前
1
0
第一个vue应用

https://www.bootcdn.cn/vue/ <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title> <style> .bg{ color: red; ......

江戸川
51分钟前
3
0
NCRE考试感想 四级嵌入式(下)

权威的官方文件 考试时间:2017年3月 经验写于:2017年5月 万事万物都在变化,四级嵌入式也是如此。所以,该经验仅作为参考,官方的文件才是权威。   做题经验 第一遍做题库时,拿眼睛看,看...

志成就
今天
2
0
共享Session

分布式系统中,Session 共享有很多的解决方案,其中托管到缓存中应该是最常用的方案之一。 spring官方说明: Spring Session 提供了一套创建和管理 Servlet HttpSession 的方案。Spring Sess...

贾峰uk
今天
2
0
秒杀

少年已不再年少
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部