三沣开发知识 购物 网址 游戏 小说 股票 美女 租车 短信 新闻 笑话 | 开发 汉字 下载 软件 图书馆
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
vbs/VBScript DOS/BAT hta htc python perl 游戏相关 VBA 远程脚本 ColdFusion ruby专题
autoit seraphzone PowerShell linux shell Lua Golang Erlang 其它教程 CSS/HTML/Xhtml
html5 CSS XML/XSLT Dreamweaver教程 经验交流 开发者乐园 Android开发资料
站长资讯 .NET新手 ASP.NET C# WinForm Silverlight WCF CLR WPF XNA VisualStudio ASP.NET-MVC .NET控件开发 EntityFramework WinRT-Metro Java C++ PHP Delphi Python Ruby C语言 Erlang Go Swift Scala R语言 Verilog 其它语言 架构设计 面向对象 设计模式 领域驱动 Html-Css JavaScript jQuery HTML5 SharePoint GIS技术 SAP OracleERP DynamicsCRM K2 BPM 信息安全 企业信息 Android开发 iOS开发 WindowsPhone WindowsMobile 其他手机 敏捷开发 项目管理 软件工程 SQLServer Oracle MySQL NoSQL 其它数据库 Windows7 WindowsServer Linux
  IT知识库 -> ASP.NET -> 破除依赖 -> 正文阅读
 

[ASP.NET]破除依赖

破除依赖   前言:单元测试的时候经常出现一个对象依耐另一个你无法控制的对象,所以这个时候你必须去替代成一个你自己可以控制的对象来摆脱依耐。
1:为什么要破除依赖 1.1:运行速度慢
比喻我们要通过用户Id判断用户是否存在,那么我们这个方法就会依赖数据库。这样就成了集成测试,如果大量的测试就会出现速度慢。
1.2:需要配置
由于依赖数据库,就会配置和数据库相关的文件。
1.3:一次性测试很多内容,无法定位错误信息
比喻这个方法错误可能是由于传入的用户Id为空,也可能传入的用户Id不存在,还可能是数据库连接断开等,这样我们就起不到我们单元测试的目的。
2:存根
一个外部依赖项:指的是系统中的一个对象,被测试的代码与这个对象发生交互,但是你不能控制这个对象。比喻前端工程师和后台工程师合作,前端工程师要等待后台返回的数据来处理,那么后台就是他的一个外部依赖项。因为他无法控制后台的代码
定义:一个存根(stub)是对系统中存在的一个依赖项(或协作者)的可控制的替代物(就是你找一个对象来替换你无法控制的对象)。通过使用存根,你在测试代码时无需直接处理这个依赖项。(说白了就是一个你自己定义来对象来取代你无法控制的对象)
3:重构代码设计来提高代码的可测试性 3.1:抽取接口使底层可替换
 其实底层我们就应该使用接口,这样上层代码依赖的是接口而不是具体的对象,使项目具有更好的扩展性,当然这里做事为了更好的测试。
从底层方法中抽出一个接口

public interface IUser
    {
        /// <summary>
        /// 检验用户是否存在
        /// </summary>
        /// <param name="userId">用户名</param>
        /// <returns></returns>
        bool IsExist(string userId);
    }

底层访问数据库的类

public class User:IUser
    {
        public bool IsExist(string userId)
        {
            //从数据库查询
            //如果有返回true
        }
    }

待测试的工作单元

public bool IsExistUser(string userId)
        {            
             var user = new User();
             return user.IsExist(userId);
        }                
        

一个可控制的存根

public class FackUser:IUser
    {
        public bool WillBevalid = false;        
        public bool IsExist(string userId)
        {
            return WillBevalid;    
        }
    }

下面开始注入存根了。
4:依赖注入(在被测试的单元中注入一个伪实现) 4.1:构造参数注入
顾名思义就是实例化的时候在构造参数的时候把伪对象注入
此时我们就要修改我们上面的类了如下
被测试类

public class UserBll
    {
        private readonly IUser _user;
        public UserBll(IUser user)
        {
            this._user = user;
        }
        public bool IsExistUser(string userId)
        {            
            return _user.IsExist(userId);                        
        }       
}

测试代码

         [Test]
        public void IsExistUser_ExistUser_ReturnsTrue()
        {
            var fackUser = new FackUser {WillBevalid = true};
            var user = new UserBll(fackUser);//注入伪对象
            bool result = user.IsExistUser("1");
            Assert.IsTrue(result);
        }

 关于构造函数注入的总结:使用构造函数注入比较简单直观可读性和理解方面也很不错。但是也有问题就是当你依赖越来越多的时候,加入构造函数的参数越来越多这样就会变得难以维护。
使用场景:比喻api的设计就是某些使用者本身就是带有参数的构造函数那么就可以这么做。
4.2:使用属性(get;set)注入伪对象
被测试类

public class UserBll
    {
        public IUser User { get; set; }

        public UserBll(IUser user)
        {
            User = new User();//默认的情况执行正常对象
        }

        public bool IsExistUser(string userId)
        {
            return User.IsExist(userId);    
        }       
}

代码测试

       [Test]
        public void IsGetName_NormalGetName_ReturnsTrue() {
            var fackUser = new FackUser { WillBevalid = true };
            var user = new UserBll { User = fackUser };//属性注入
            bool result = user.IsExistUser("1");
            Assert.IsTrue(result);
        }

关于属性注入总结:和构造函数注入相似不过更易读,更易编写。
什么时候使用属性注入:想表明哪个被测试类的某个依赖项是可选的,或者测试可以放心使用默认创建的这个依赖项,就可以属性注入
4.3:在工厂中伪造一个成员(伪对象)
我们先看工厂类

public class UserFactory
    {
        private  IUser _user = null;

        public  IUser Create()
        {
            if (_user != null)
                return _user;
            return new User();
        }

        [Conditional("DEBUG")]
        public  void SetUser(IUser muser)
        {
            _user = muser;
        }
    }

被测试类

public class UserBll
    {        
        public bool IsExistUser(string userId)
        {                            
                var userFactory = new UserFactory();                                          
return userFactory.Create().IsExist(userId); }

测试代码

        [Test]
        public void IsGetName_NormalGetName_ReturnsTrue() {
            var fackUser = new FackUser { WillBevalid = true };
            var userFactory = new UserFactory();
            userFactory.SetUser(fackUser);//设置自己要注入的伪对象
            bool result = new UserBll().IsExistUser("1");
            Assert.IsTrue(result);
        }

关于伪造方法的总结: 这种方法很简单,对工厂添加一个你要控制的伪依赖项。对被测试代码没什么改变一切还是原样。
这种方式明显比前两种好。相当于加入了一个工厂的缓冲区。在这里可以做一些逻辑上的处理。
4.4:抽取和重写
 使用这种方法的步骤:
在被测试类:
添加一个返回真真实实的虚工厂的方法; 在正常的代码中使用工厂方法
在测试项目中:
创建一个新类 声明这个新类继承被测试类 创建一个你要替换的接口类型的公共字段(不需要属性) 重写虚方法 返回公共字段
在测试代码中:
创建一个存根类的实例。此存根实现所要求的接口 创建新派生类而非测试类的实例
伪造一个工厂方法

public class UserBll
    {        
        public bool IsExistUser(string userId)
        {            var user = UserManager();
                return user.IsExist(userId);                     
        }

        protected virtual IUser UserManager()
        {        
            return new User();
        }

创建新类并集成被测试类

public class TestUser : UserBll
    {
        public TestUser(IUser user) {
            _muser = user;
        }

        private readonly IUser _muser;

        protected override IUser UserManager() {
            return _muser;
        }        
    }

测试代码:

        [Test]
        public void IsGetName_NormalGetName_ReturnsTrue() {
            var fackUser = new FackUser { WillBevalid = true };//存根实例
            var testUser = new TestUser(fackUser);//注入伪对象(新派生的类)
            bool result = testUser.IsExistUser("1");
            Assert.IsTrue(result);
        }

关于抽取和重写注入的总结:写更少的接口,代码更容易替换。我觉得这种方法最好,就是留了一条路,不光对于测试,如果哪天发现这代码不好了,直接可以在底层新添加一个替换即可,不会影响原来的代码
什么时候使用:当你调用外部依赖项时候想模拟自己想要的值的时候就特别受用。
4.5:重构技术变种
先看被测试类

public class UserBll
    {        
        public bool IsExistUser(string userId)
        {
return UserManager(userId);         
        }

        protected virtual bool UserManager(string userId)
        {
            IUser user = new User();
            return user.IsExist(userId);
        }        
    }

创建新类并集成被测试类

public class TestUser : UserBll
    {    
        public bool IsSupported;
        protected bool IsGetUserName(string userId) {
            return IsSupported;
        }
    }

测试类

public void IsGetName_NormalGetName_ReturnsTrue() {
            var testUser = new TestUser { IsSupported = true };
            bool result = testUser.IsExistUser("1");
            Assert.IsTrue(result);
        }

总结:这和上一种方式其实是很像的,只不过这种更彻底。这种方式更加简单。不在添加很多的构造函数,设置方法或者工厂类。不过确实不符合面向对象中的封装原则。暴露了用户不改看到的东西。
各种依赖注入灵活使用。个人觉得后三种都不错。
  ASP.NET 最新文章
C#消息队列(MQ)零基础从入门到实战演练
ASP.NET前台html页面对table数据的编辑删除
Azure 12 月新公布
万物互联时代,怎能少得了它?
「金融级」大数据云服务,其实并非那么高不
手握不同媒体的数据,接下来该干些什么?
Jenkins部署.net自动化构建
asp.net5开发中DNX SDK版本的影响
asp.netMD5加密代码
一个技术汪的开源梦 —— 公共组件缓存之分
上一篇文章      下一篇文章      查看所有文章
加:2015-12-02 16:42:49  更:2017-05-14 07:43:38 
 
技术频道: 站长资讯 .NET新手区 ASP.NET C# WinForm Silverlight WCF CLR WPF XNA Visual Studio ASP.NET MVC .NET控件开发 Entity Framework WinRT/Metro Java C++ PHP Delphi Python Ruby C语言 Erlang Go Swift Scala R语言 Verilog 其它语言 架构设计 面向对象 设计模式 领域驱动设计 Html/Css JavaScript jQuery HTML5 SharePoint GIS技术 SAP Oracle ERP Dynamics CRM K2 BPM 信息安全 企业信息化其他 Android开发 iOS开发 Windows Phone Windows Mobile 其他手机开发 敏捷开发 项目与团队管理 软件工程其他 SQL Server Oracle MySQL NoSQL 其它数据库 Windows 7 Windows Server Linux
脚本语言: vbs/VBScript DOS/BAT hta htc python perl 游戏相关 VBA 远程脚本 ColdFusion ruby专题 autoit seraphzone PowerShell linux shell Lua Golang Erlang 其它教程
网站开发: CSS/HTML/Xhtml html5 CSS XML/XSLT Dreamweaver教程 经验交流 开发者乐园 Android开发资料
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 美食菜谱 新闻资讯 电影视频 小游戏 Chinese Culture 股票 租车
生肖星座 三丰软件 视频 开发 短信 Android开发 站长 古典小说 网文精选 搜图网 美图 中国文化英文版 多播 租车 短信
2017-8-19 3:55:53
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT知识库