国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

破除依賴

2019-11-14 14:25:12
字體:
來源:轉載
供稿:網友

  前言:單元測試的時候經常出現一個對象依耐另一個你無法控制的對象,所以這個時候你必須去替代成一個你自己可以控制的對象來擺脫依耐。

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);        }

總結:這和上一種方式其實是很像的,只不過這種更徹底。這種方式更加簡單。不在添加很多的構造函數,設置方法或者工廠類。不過確實不符合面向對象中的封裝原則。暴露了用戶不改看到的東西。

各種依賴注入靈活使用。個人覺得后三種都不錯。

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 大城县| 大同县| 苍山县| 江口县| 万盛区| 浏阳市| 徐水县| 子洲县| 福建省| 吴川市| 三亚市| 昭觉县| 定西市| 铜川市| 利川市| 土默特右旗| 根河市| 金塔县| 衡阳县| 汽车| 南投县| 贡嘎县| 彭泽县| 天峨县| 五指山市| 东至县| 饶阳县| 邳州市| 大足县| 曲靖市| 黔南| 甘肃省| 延安市| 余干县| 镶黄旗| 柳河县| 醴陵市| 武城县| 龙川县| 永丰县| 抚顺县|