<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      Fork me on GitHub

      基于VS2012 Fakes框架的TDD實(shí)戰(zhàn)——私有成員,靜態(tài)成員模擬

      前言

        上文書(基于VS2012 Fakes框架的TDD實(shí)戰(zhàn)——接口模擬)把接口模擬的部分演示完了,接口模擬也是Mock框架最基本的功能了吧,比如很易用的Moq框架,就非常容易模擬出接口中定義的操作返回的結(jié)果。

        Moq也有局限性,比如不能模擬密封類,不能直接模擬靜態(tài)方法等,而這些需求在微軟VS2012帶來(lái)的Fakes框架中都能得到很好的解決。

      需求說(shuō)明

        一個(gè)項(xiàng)目的開發(fā)中,最見怪不怪的就是需求的變更了,比如我們這個(gè)用戶名重復(fù)性檢查的功能,它就變了,變化如下:

      • 給未激活用戶信息添加有效期屬性,防止用戶名被惡意占用

       

      準(zhǔn)備工作

        修改MemberInactive類如下:

       1     public class MemberInactive : Entity
       2     {
       3         public string UserName { get; set; }
       4 
       5         public string Password { get; set; }
       6 
       7         public string Email { get; set; }
       8 
       9         /// <summary>
      10         ///   激活過(guò)期時(shí)間
      11         /// </summary>
      12         public DateTime Expiration { get; set; }
      13     }

      開工

      1. 編寫測(cè)試用例與實(shí)現(xiàn)代碼

        先編寫一個(gè)檢查未激活用戶信息有效性的方法的測(cè)試用例,現(xiàn)在是2012年8月26日,所以定MemberInactive的過(guò)期時(shí)間為2012年8月27日。方便起見,我們先把IsMemberInactiveValid方法的可訪問(wèn)性定為public,用使可以用原來(lái)的方式來(lái)進(jìn)行測(cè)試
        1         [TestMethod]
        2         public void IsMemberInactiveValid_有效的_過(guò)期時(shí)間大于當(dāng)前時(shí)間()
        3         {
        4             var memberInactive = new MemberInactive { Expiration = new DateTime(2012, 8, 27) };
        5             Assert.IsTrue(_accountService.IsMemberInactiveValid(memberInactive));
        6         }

         在類AccountService編寫IsMemberInactiveValid方法讓測(cè)試通過(guò)

        1         public bool IsMemberInactiveValid(MemberInactive memberInactive)
        2         {
        3             var dtNow = DateTime.Now;
        4             return memberInactive.Expiration.CompareTo(dtNow) >= 0;
        5         }

      2. 靜態(tài)屬性的模擬

        測(cè)試通過(guò)了,但上面的測(cè)試用例有個(gè)問(wèn)題,今天能跑通過(guò),后天呢,到了28號(hào),就注定是失敗的了,因?yàn)閷?shí)現(xiàn)方法中有一個(gè)外部依賴DateTime.Now,自動(dòng)化測(cè)試中,方法體范圍內(nèi)的所有外部依賴都應(yīng)該被模擬即你要測(cè)的僅是這個(gè)方法內(nèi)的代碼的正確性,不應(yīng)該受外界影響。現(xiàn)在我們來(lái)模擬DateTime.Now,這是一個(gè)靜態(tài)的公共屬性。在mscorlib.dll程序集System命名空間下實(shí)現(xiàn)的。所以需要?jiǎng)?chuàng)建System的Fakes程序集。靜態(tài)成員的模擬將用到Shim類型的模擬類(Fakes框架生成的模擬類有兩種,Stub和Shim,具體請(qǐng)參考官方文檔)

        修改上面的測(cè)試用例如下:

         1         [TestMethod]
         2         public void IsMemberInactiveValid_有效的_過(guò)期時(shí)間大于當(dāng)前時(shí)間()
         3         {
         4             var memberInactive = new MemberInactive { Expiration = new DateTime(2012, 8, 27) };
         5             //Assert.IsTrue(_accountService.IsMemberInactiveValid(memberInactive));
         6             using (ShimsContext.Create())
         7             {
         8                 ShimDateTime.NowGet = () => new DateTime(2012, 8, 26);
         9                 Assert.IsTrue(_accountService.IsMemberInactiveValid(memberInactive));
        10             }
        11         }

        第8行即模擬了DateTime.Now的返回值,這時(shí),即使你把系統(tǒng)時(shí)間修改為28號(hào),這個(gè)測(cè)試也能通過(guò),因?yàn)楝F(xiàn)在測(cè)試的運(yùn)行已經(jīng)與系統(tǒng)時(shí)間無(wú)關(guān)了。

      3. 私有方法的測(cè)試

        上面的例子為了承接上篇寫測(cè)試用例的方法把IsMemberInactiveValid方法設(shè)成了public,但實(shí)際上這個(gè)方法應(yīng)該是私有的,現(xiàn)在把方法的可訪問(wèn)性改為private,原來(lái)的測(cè)試用例當(dāng)然是不能通過(guò)的,因?yàn)檫@個(gè)方法找不到了。把測(cè)試用例改為如下:
                [TestMethod]
                public void IsMemberInactiveValid_有效的_過(guò)期時(shí)間大于當(dāng)前時(shí)間()
                {
                    var memberInactive = new MemberInactive { Expiration = new DateTime(2012, 8, 27) };
                    using (ShimsContext.Create())
                    {
                        ShimDateTime.NowGet = () => new DateTime(2012, 8, 26);
                        var po = new PrivateObject(new AccountService());
                        var result = po.Invoke("IsMemberInactiveValid", new object[] {memberInactive});
                        Assert.IsTrue((bool) result);
                    }
                }

        測(cè)試通過(guò),私有成員的訪問(wèn)用到了PrivateObject,其實(shí)這個(gè)類也沒什么奇特的地方,只是封裝了反射的相關(guān)操作,讓我們調(diào)用更方便些

      4. 私有方法的模擬

        在把調(diào)用IsMemberInactiveValid的代碼加入U(xiǎn)serNameExistsCheck方法之前,千萬(wàn)別忘記了在測(cè)試類初始化的代碼中把IsMemberInactiveValid模擬出來(lái),否則加入之后原來(lái)的測(cè)試用例就有可能無(wú)法通過(guò)了。下面這個(gè)測(cè)試用例就通不過(guò)了
        1         [TestMethod]
        2         public void UserNameExistsCheck_用戶存在_用戶在用戶數(shù)據(jù)庫(kù)中不存在_and_注冊(cè)需要激活_用戶在未激活用戶數(shù)據(jù)庫(kù)中存在()
        3         {
        4             var userName = "柳柳英俠";
        5             var configName = "configName";
        6             _member = null;
        7             _configInfo.RegisterConfig.NeedActive = true;
        8             Assert.IsTrue(_accountService.UserNameExistsCheck(userName, configName));
        9         }

        不過(guò)這個(gè)問(wèn)題先放下,我們先來(lái)看看私有成員應(yīng)該怎樣來(lái)模擬,將用到AccountService類的模擬類,因?yàn)檫@個(gè)私有方法是這個(gè)類的成員,如下的測(cè)試用例:

         1         [TestMethod]
         2         public void UserNameExistsCheck_用戶存在_用戶在用戶數(shù)據(jù)庫(kù)中不存在_and_注冊(cè)需要激活_and_用戶在未激活用戶數(shù)據(jù)庫(kù)中存在_and_未激活用戶信息有效()
         3         {
         4             var userName = "柳柳英俠";
         5             var configName = "configName";
         6             _member = null;
         7             _configInfo.RegisterConfig.NeedActive = true;
         8             using (ShimsContext.Create())
         9             {
        10                 ShimAccountService.AllInstances.IsMemberInactiveValidMemberInactive = (@accountService, @memberInactive) => true;
        11                 Assert.IsTrue(_accountService.UserNameExistsCheck(userName, configName)); 
        12             }
        13         }

         根據(jù)測(cè)試用例修改UserNameExistsCheck方法代碼如下(第26行)

         1         public bool UserNameExistsCheck(string userName, string configName)
         2         {
         3             if (string.IsNullOrEmpty(userName))
         4             {
         5                 throw new ArgumentNullException("userName");
         6             }
         7             if (string.IsNullOrEmpty(configName))
         8             {
         9                 throw new ArgumentNullException("configName");
        10             }
        11             var member = MemberDao.GetByName(userName);
        12             if (member != null)
        13             {
        14                 return true;
        15             }
        16             var configInfo = ConfigInfoDao.GetByName(configName);
        17             if (configInfo == null)
        18             {
        19                 throw new NullReferenceException("系統(tǒng)配置信息為空。");
        20             }
        21             if (!configInfo.RegisterConfig.NeedActive)
        22             {
        23                 return false;
        24             }
        25             var memberInactive = MemberInactiveDao.GetByName(userName);
        26             if (memberInactive != null && IsMemberInactiveValid(memberInactive))
        27             {
        28                 return true;
        29             }
        30             return false;
        31         }

         測(cè)試通過(guò)。
        現(xiàn)在來(lái)解決那個(gè)未通過(guò)的測(cè)試用例,按照TDD的原則,我們不能去修改測(cè)試用例來(lái)使它通過(guò)。
        不能通過(guò)的原因也就是IsMemberInactiveValid的模擬沒有在測(cè)試類中進(jìn)行初始化,下面我們來(lái)初始化它。由上面的測(cè)試用例可以看到,在私有成員的模擬中,測(cè)試方法的執(zhí)行結(jié)果必須放在using語(yǔ)句中,而using語(yǔ)句實(shí)質(zhì)也就是自動(dòng)化了IDisposable接口,所以我們完全可以把它拆開,然后手動(dòng)調(diào)用Dispose即可
        在測(cè)試類AccountServiceTest添加一個(gè)私有字段來(lái)存儲(chǔ)ShimsContext.Create(),一個(gè)私有字段存儲(chǔ) IsMemberInactiveValid的模擬結(jié)果:

        1     private IDisposable _shimsContext = ShimsContext.Create();
        2     private bool _isMemberInactiveValid = true;

        在標(biāo)記[TestInitialize]的MyTestInitialize方法中添加 IsMemberInactiveValid 方法的模擬

        1     ShimAccountService.AllInstances.IsMemberInactiveValidMemberInactive = (@accountService, @memberInactive) => _isMemberInactiveValid;

        取消標(biāo)記[TestCleanup()]的MyTestCleanup方法的注釋,添加 ShimsContext.Create() 的  Dispose 調(diào)用

        1         // 在每個(gè)測(cè)試運(yùn)行完之后,使用 TestCleanup 來(lái)運(yùn)行代碼
        2         [TestCleanup()]
        3         public void MyTestCleanup()
        4         {
        5             _shimsContext.Dispose();
        6         }

        這樣,初始化完畢,再運(yùn)行全部測(cè)試用例,全綠,心情大好└(^o^)┘

        至此,我認(rèn)為要講的大概都講到了,當(dāng)然,F(xiàn)akes框架還有很多功能, 有待大家挖掘。

      總結(jié)

      總結(jié)說(shuō)點(diǎn)什么呢,總結(jié)一下TDD吧

      開發(fā)過(guò)程:

      1. 快速新增一個(gè)測(cè)試
      2. 運(yùn)行所有的測(cè)試(有時(shí)候只需要運(yùn)行一個(gè)或一部分),發(fā)現(xiàn)新增的測(cè)試不能通過(guò)
      3. 做一些小小的改動(dòng),盡快地讓測(cè)試程序可運(yùn)行,為此可以在程序中使用一些不合情理的方法
      4. 運(yùn)行所有的測(cè)試,并且全部通過(guò)
      5. 重構(gòu)代碼,以消除重復(fù)設(shè)計(jì),優(yōu)化設(shè)計(jì)結(jié)構(gòu)

      優(yōu)點(diǎn):

      1. 在開發(fā)過(guò)程的任意時(shí)刻,都可以生成一個(gè)可以使用,具有一定功能,Bug較少的測(cè)試版本
      2. 新增的功能不會(huì)破壞已有功能
      3. 測(cè)試用例已經(jīng)包含業(yè)務(wù)需求和規(guī)則,是最符合實(shí)際,與時(shí)俱進(jìn)的開發(fā)文檔
      4. 規(guī)則長(zhǎng)期保留并明確

      缺點(diǎn):

      1. 代碼量大大增加(其實(shí)只是把需求先體驗(yàn)在代碼上,傳統(tǒng)的開發(fā)方式先需求體驗(yàn)在腦中)
      2. 憑空編寫測(cè)試用例(其實(shí)并不憑空,只需要把代碼運(yùn)行的環(huán)境,涉及的底層模塊想清楚,就能立即體現(xiàn)到測(cè)試用例上)

      其實(shí)說(shuō)白了,TDD與先代碼后測(cè)試的開發(fā)方式的區(qū)別,只是前者是把寫代碼前在腦中的想法體現(xiàn)在測(cè)試用例上,一個(gè)動(dòng)手寫了,一個(gè)在腦中構(gòu)思而已,就這么簡(jiǎn)單,只要立即動(dòng)手把想法以測(cè)試用例展現(xiàn)出來(lái),就跨出TDD的第一步了,勇敢的跨出第一步吧

      源碼下載

       LiuliuTDDFakesDemo02.rar

       

       

      posted @ 2012-08-26 17:38  郭明鋒  閱讀(6821)  評(píng)論(4)    收藏  舉報(bào)

      主站蜘蛛池模板: 亚洲精品福利一区二区三区蜜桃| 扒开双腿疯狂进出爽爽爽| 无套内射视频囯产| 四虎影视www在线播放| 国产性三级高清在线观看| 无码国模国产在线观看免费| 国产成人午夜精品永久免费 | 深夜免费av在线观看| 日韩av日韩av在线| 欧美精品亚洲精品日韩专区| 日韩人妻不卡一区二区三区| 亚洲欧美日韩成人综合一区| 久久精品国产中文字幕| 99久久国产综合精品色 | 中文字幕日韩一区二区不卡| 中国丰满少妇人妻xxx性董鑫洁| 韩国 日本 亚洲 国产 不卡| 香蕉EEWW99国产精选免费| 国产又大又粗又爽的毛片| 成人午夜在线观看刺激| 无码欧亚熟妇人妻AV在线外遇| 中文字幕人妻精品在线| 搡老熟女老女人一区二区| 国产亚洲精品自在久久蜜TV| 亚洲成人高清av在线| 少妇伦子伦情品无吗| 一二三四中文字幕日韩乱码| 色综合激情丁香七月色综合| 成人影片麻豆国产影片免费观看| 欧美喷潮最猛视频| 超清无码一区二区三区| 久久亚洲精品亚洲人av| 国产色无码专区在线观看| 安福县| 国产一区二区三区精品综合| 久久这里只有精品免费首页| 一区二区在线观看成人午夜 | 国产福利永久在线视频无毒不卡 | 国产精品自拍视频我看看| gogogo高清免费观看| 黄色三级亚洲男人的天堂|