zoukankan      html  css  js  c++  java
  • Asp.net WebApi + EF 单元测试架构 DbContext一站到底

    其实关于webapi和Ef service的单元测试我以前已经写过相关文章,大家可以参考:

    Asp.net WebAPI 单元测试

    单元测试 mock EF 中DbContext 和DbSet Include

    先看一下项目结构图:

    这个demo非常简单,UTWebApi.Data 是纯粹的数据定义,UTWebApi.Service是我们的业务服务逻辑层,UTWebApi 是我们webapi的实现,UTWebApi.Tests就是测试项目。

    数据层:

    BloggerDbContext的构造函数一般都是一个,有些时候也会有多个,如:

    如果你的DbContext包含数据库中所有的表,那么只要第一个构造函数就可以了,但是如果你的表在几个DbContext中,那么第二构造函数可能需要了, 比如你需要同时操作10张表,那么这10张表的操作应该在同一个事务里面吧,但是他们分布在2个DbContext里面,所以这2个DbContext应该用一个连接。

     internal class ArticleConfiguration : EntityTypeConfiguration<Article>实体的配置类不应该是public。

    服务层:

    我们首先需要一个基类的service如下

    当然很多项目开发的时候喜欢用Repository模式, 我这里也简单实现如下:

    而我们具体的服务实现也就很简单了   public ArticleService(BloggerDbContext ctx) : base(ctx) { }

    webapi层:

    Asp.net WebAPI 单元测试 里面webapi的IOC 使用Unity.WebApi 对应的测试用的是Autofac.WebApi2,这次我们换成StructureMap(今天在公司尝试webapi里面用Unity,发现以前它的service也在用unity,但是以前用的是2.1,现在unity.webapi用的是5.1,所以要用unity还要改以前服务层的code,于是乎就用StructureMap)。需要开发2个帮助类:

        public class StructureMapScope : IDependencyScope
        {
            private readonly IContainer container;
    
            public StructureMapScope(IContainer container)
            {
                if (container == null)
                {
                    throw new ArgumentNullException("container");
                }
                this.container = container;
            }
    
            public object GetService(Type serviceType)
            {
                if (serviceType == null)
                {
                    return null;
                }
    
                if (serviceType.IsAbstract || serviceType.IsInterface)
                {
                    return this.container.TryGetInstance(serviceType);
                }
    
                return this.container.GetInstance(serviceType);
            }
    
            public IEnumerable<object> GetServices(Type serviceType)
            {
                return this.container.GetAllInstances(serviceType).Cast<object>();
            }
    
            public void Dispose()
            {
                this.container.Dispose();
            }
        }
    
        public class StructureMapDependencyResolver : StructureMapScope, IDependencyResolver
        {
            private readonly IContainer container;
    
            public StructureMapDependencyResolver(IContainer container)
                : base(container)
            {
                this.container = container;
            }
    
            public IDependencyScope BeginScope()
            {
                var childContainer = this.container.GetNestedContainer();
                return new StructureMapScope(childContainer);
            }
        }
    View Code

    具体使用如下:

    api运行结果如图:

    测试项目:

    首先测试需要mock DBContext,大家可以参考单元测试 mock EF 中DbContext 和DbSet Include 来做,其次我们需要一个DbHelper,用它来货物DbContext 和初始化数据:

    那么测试项目的code 也就非常简单:

    看这里的测试已经通过了, 这个DbContext是所有的测试共用的,就如同我们的数据库一样,尤其是DbContext的表比较多(500以上),那么mock这个DbContext需要花费比较长的时间。

    整个项目的结构就说完了,一般我们的controller的ioc都是做服务层,而服务层又需要ioc数据层,就像我前面的Asp.net WebAPI 单元测试里面的GetIArticleService方法,其实现还是比较累的,太多的mock了,

    同时在controller那里的ioc 的code也就比较多了,每增加一个controller就需要增加一个对应的service,那么ioc对应的配置也要增加 是不是很麻烦了? 所以索性直接ioc DbContext。因为在我们很多实际项目中service中的code 重用性不是很高,每当增加controller的时候,一般都要增加service去实现相应的逻辑,比如一个查询逻辑现在有3个service已经实现了,但是在controller你可能不直接用这3个service, 而是重新建一个service,把3个EF 查询合并成一个,这样EF访问3次DB 就变为一次,并且不需要的数据也不会返回了。所以这里的DbContext真的是一站到底, 从数据层经过服务层最后坚持到接口层,而单元测试的难点就在如何mock DbContext,这里我们采用万剑归中的方式来做(表达式给DbContext的DbSet赋值)。

     下载地址

  • 相关阅读:
    Json -- 语法和示例,javascript 解析Json
    平衡二叉树的实现原理
    递归:汉诺塔
    递归:这帮坑爹的小兔崽子
    函数:递归是神马
    函数:lambda表达式
    函数:内嵌函数和闭包
    函数:我的地盘听我的
    函数:灵活即强大
    函数:Python的乐高积木
  • 原文地址:https://www.cnblogs.com/majiang/p/5585572.html
Copyright © 2011-2022 走看看