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

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

模擬對象和存根

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

 在單元測試中存根和模擬對象處于一個非常重要的地位以下我就來說說我對兩者的理解。

1:什么是交互測試

工作單元最終的三種結果類型:

  • 基于值的測試:驗證函數返回值
  • 基于狀態的測試:驗證通過被測試函數之后狀態的變化
  • 交互測試:驗證一個對象如何向另一個對象(一般是第三方服務)發送消息(調用方法)

重點說一下交互測試:如果一個特定的工作單元最終的結果就是調用另一個對象那么就要進行交互測試。簡單來說就是你無法判斷你是否調用了這個方法,因為他的返回值是void,那么你只能通過其他方式來判斷你確實調用了這個方法。這整個過程就就是交互測試。

2:模擬對象

什么是模擬對象:模擬對象也是偽對象,它可以驗證被測試對象是否按照預期的方式調用了這個偽對象,因此來判斷單元測試的成功或者失敗。

舉一個例子:小明由于作業沒有做完,老師就讓小明放學之后晚回家一個小時來寫作業,那么今天老師有事就提前回家了,就讓班長小亮來查看小明是否留下來一個小時在寫作業。此時小亮就是我們說的偽對象,他就檢測了小明是否晚回家一個小時。

3:模擬對象和存根的區別

1:存根上一篇已經說了現在我們看一下圖

很明顯我們斷言的對象是被測試類下面是模擬對象

這個是對模擬對象的斷言。

其實他們區別很小,他們的根本區別就是存根不會導致測試的失敗而模擬對象卻可以(存根由于斷言是對被測試類所以不會導致測試失敗,而模擬對象恰恰相反)

模擬對象就是來檢測你的測試是否會失敗。下面看例子

4:模擬對象和存根的使用

現在我引用一個外部的LogService專門記錄錯誤日志,但是這個日志是void類型無法返回這個時候模擬對象就派上用場。我們先定義個一個日志服務接口

public interface ILogService    {        void ErrorLog(string message);    }

被測試代碼

PRivate readonly ILogService _logService;        public UserManager(ILogService logService)//模擬對象注入        {            this._logService = logService;        }        public void RecordLog(string userName)        {            if (userName.Length<=4)//名稱長度小于4就記錄日志異常            {                _logService.ErrorLog(userName);            }        }

模擬對象

public class FakeLogService:ILogService    {        public string Message;//記錄錯誤信息        public void ErrorLog(string message)        {            this.Message = message;        }    }

測試代碼

[Category("模擬對象")]        [Test]        public void RecordLog_UserNameTooShort_CallLogService()        {            var mockService=new FakeLogService();            var userManager=new UserManager(mockService);//注入模擬對象            userManager.RecordLog("lp");//記錄錯誤日志            Assert.AreEqual("lp", mockService.Message);//如果錯誤信息和模擬對象的相同說明我已經調用了這個方法并正確的傳遞了值        }

我們看一下測試效果:

我們發現測試過去了說明我們已經的方法正確的調用和傳遞值給日志服務。這個測試保證的是我們調用日志方法沒有錯誤。

存根和模擬對象的同時使用

有時候一個方法體有2個未能返回值的方法,那么這個時候你可能就要確定一下哪個是存根哪個是模擬對象了。

比喻現在我們又加入一個需求,如果出現錯誤日志異常就要給系統管理員發一份郵件,這個時候我們就發現自己有2個沒有返回值的函數,不建議寫2個模擬對象,那樣就會造成混亂你不知道到底是哪個方法出現錯誤(因為斷言是針對于模擬對象的)

先定義一個郵件發送接口

public interface IEmailService    {        void SendEmail(string user, string subject, string content);    }

模擬對象實現這個接口

public class FackEmailService : IEmailService    {        public string User { get; set; }        public string Subject { get; set; }        public string Content { get; set; }        public void SendEmail(string user, string subject, string content)        {            this.User = user;            this.Subject = subject;            this.Content = content;        }    }

我們在看看被測試的代碼

public class UserManager    {        public void RecordLog(string userName)        {            try            {                if (userName.Length <= 4) //名稱長度小于4就記錄日志異常                {                    LogManager().ErrorLog(userName);                }            }            catch (Exception ex)            {                EmailService().SendEmail("lp", "subject", ex.Message);            }        }        protected virtual ILogService LogManager()//底層可替換        {            return new LogService();        }
protectedvirtual IEmailService EmailService()
{
returnnew EmailService();
} }

創建新類繼承被測試類來完成底層替換

    public class TestUserManager : UserManager    {        private readonly ILogService _logService;        private readonly IEmailService _emailService;        public TestUserManager(ILogService logService, IEmailService emailService)        {            _logService = logService;            _emailService = emailService;        }        public override IEmailService EmailService()        {            return _emailService;        }        public override ILogService LogManager()        {            return _logService;        }    }

測試代碼

       [Test]        public void RecordLog_EmailServiceThrows_SendsEmail()        {            var stubLogService = new FakeLogService() {Exception = new Exception("fack exception")};//日志的模擬對象拋出異常(這個是存根)            var mokeEmailService = new FackEmailService();            var testUser = new TestUserManager(stubLogService, mokeEmailService);//注入存根和模擬對象            testUser.RecordLog("lp");            StringAssert.Contains("lp",mokeEmailService.User);            StringAssert.Contains("subject",mokeEmailService.Subject);            StringAssert.Contains("fack exception",mokeEmailService.Content);        }

我們看看測試結果

你也可以把三個屬性封裝成一個實體對實體進行斷言。

 5:偽對象鏈

什么是對象鏈:就是一個對象的屬性是另一個對象然后這個對象的屬性又是一個對象。比喻我們經常看到的ConfigurationManager.ConnectionStrings[0].ConnectionString這個就是一個對象鏈。

如果我們在測試的時候就發現需要偽造2個對象如果很多的話就可能偽造的更多,所以我們在重構代碼的時候就要考慮可測的代碼如下這樣就測試就可以直接替代

    protected virtual string GetConnectionString()        {            return ConfigurationManager.ConnectionStrings[0].ConnectionString;        }

6:手工模擬對象和存根的存在的問題

  • 編寫模擬對象和存根耗時間
  • 如果接口有很多方法、屬性、事件編寫的時候會特別困難
  • 如果驗證調用者向另一個方法調用傳遞的所有參數都是正確的時候就需要多次進行斷言。
  • 有些模擬對象就是為特定的方法編寫復用性比較差

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 鄂托克前旗| 全南县| 弥渡县| 汶川县| 益阳市| 周口市| 务川| 辽中县| 靖西县| 东阳市| 亳州市| 乐昌市| 平顶山市| 新丰县| 交城县| 西贡区| 陆丰市| 镇远县| 天柱县| 静海县| 昌都县| 开化县| 衡阳市| 嘉峪关市| 临海市| 新宾| 南投县| 柘城县| 陇西县| 德阳市| 通辽市| 铜鼓县| 钦州市| 眉山市| 卫辉市| 高台县| 河东区| 昔阳县| 永吉县| 三河市| 景谷|