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

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

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

      如何 Mock 非虛方法和密封類?

      更好的排版建議到我的博客中閱讀:

      http://www.dozer.cc/2012/11/how-to-mock-non-virtual-method-and-sealed-class/

       

      問題

      很常見的問題,沒有接口,那如何 Mock 非虛方法和密封類?

      我在上一篇文章(單元測試有感)中介紹了單元測試的原則,也提到了一些技巧,但是代碼是以前寫的,總會有很多不能克服的地方,還有也不可能把所有的方法改成 vitrual ,或者所有的類都有接口。

       

      尋找

      一開始搜索:mock non-virtual ,找到了一篇文章:傳送門

      文中提到了一個神器:Typemock,貌似它可以實現,但是它是收費的…

      大致看了看它的原理,和 PostSharp (PostSharp是用來做 AOP 的)差不多,都是會去修改編譯完成的 dll 文件,簡單粗暴!

      雖然粗暴,但貌似的確是一個不錯的方法,別的項目正常編譯,Test 項目中為了測試,把所有的方法加上 Virtual 關鍵字,這樣不就行了嗎?

      思路是清晰了,可惜工具都是收費了,直到看到了老趙的博客。

      上面的鏈接是老趙的原文,可惜他好像誤操作,被另一篇文章替換了。

      下面的鏈接是別人轉載的,可以看,雖然代碼縮進都不對。

      另外,老趙的文章提供了一個很好的思路,但是沒有后續具體的操作細節,我也摸索了很久。所以,下面我會給大家介紹一下完整的、具體的實現步驟。

       

      實現思路和技術細節

      實現思路其實收費的 Mock 工具已經提供了:

      1. 項目中按照之前的設計原則,編寫自己的代碼;
      2. 測試項目每次編譯完成后,運行一個程序,修改需要 Mock 的 dll ;
      3. 利用 Moq 等 Mock 框架,在運行時動態生成代理類;

      這里,只會修改復制到 Test 運行目錄的 dll,所以不會影響別的項目。

       

      技術細節的話,這里就需要用到老趙博客中提到的 Mono.Cecil 了,建議用 NuGet 獲取最新版本。

      Mono.Cecil 可以幫助你修改編譯好的 dll 文件。

      核心代碼如下(這部分邏輯由老趙提供,我做了一定的修改):

      private static void OverWrite(string file, bool hasSymbols)
      {
          var asmDef = AssemblyDefinition.ReadAssembly(file, 
                                new ReaderParameters { ReadSymbols = hasSymbols });
          var classTypes = asmDef.Modules
                                  .SelectMany(m => m.Types)
                                  .Where(t => t.IsClass)
                                  .ToList();
      
          foreach (var type in classTypes)
          {
              if (type.IsSealed)
              {
                  type.IsSealed = false;
              }
      
              foreach (var method in type.Methods)
              {
                  if (method.IsStatic) continue;
                  if (method.IsConstructor) continue;
                  if (method.IsAbstract) continue;
      
                  if (!method.IsVirtual)
                  {
                      method.IsVirtual = true;
                      method.IsNewSlot = true;
                      method.IsReuseSlot = false;
                  }
                  else
                  {
                      method.IsFinal = false;
                  }
              }
          }
      
          asmDef.Write(file, new WriterParameters { WriteSymbols = hasSymbols });
      }

      只要把這個代碼封裝成一個控制臺應用程序,每次編譯測試項目后運行一下即可。

       

      具體實現步驟

      源代碼解決方案結構

      MockHelper 是核心工具,作用就是修改編譯好的 dll,一般情況下也只要使用這個即可,別的幾個項目只是用來演示的。

      TestDll 內包含了一個密封類和非虛函數,后面會用這個做演示,把它變成可以 Mock 的。

      Test 項目就是一個 MSTest 項目,里面演示了怎么使用 MockHelper。

      NUnit 項目同樣是一個演示的測試項目,但是用的是 NUnit。

       

      MockHelper 的使用

      這個控制臺應用程序其實沒有什么難度,核心代碼上面已經貼出來了。

      另外使用的時候需要復制 MockHelper.exe、mock.txt 和 Mono.Cecil*.dll 到你的測試項目中,一共六個文件。

      使用方法就是直接運行這個控制臺應用程序,然后可以傳入一個參數:代表 dll 所在的文件夾。如果不傳參數的話默認是在運行目錄。

      然后把你需要修改的 dll 全部寫到 mock.txt 中。

       

      配置自動運行 MockHelper

      把 MockHelper 復制過去后的關鍵就是要讓這個 exe 可以自動運行啦!

      這里用的是:后期生成事件命令行

      右擊項目 — 屬性 — 生成事件 — 后期生成事件命令行:

      "$(ProjectDir)MockHelper\MockHelper.exe"

      這里不用傳參數,因為運行這個工具的是 Test 項目,而這個項目默認的運行位置就是 bin/Debug|Release,所以需要修改的 dll 就在下面。

       

      編寫測試代碼

      TestDll 是非虛函數,而且是密封類:

      public sealed class TestClass : TestClassBase
      {
          public string NormalMethod()
          {
              return "TestClass";
          }
      
          public override string VirtualMethod()
          {
              return base.VirtualMethod();
          }
      
          public sealed override string SealedMethod()
          {
              return base.VirtualMethod();
          }
      
          public override string AbstractMethod()
          {
              return "TestClass";
          }
      }
      
      public abstract class TestClassBase
      {
          public virtual string VirtualMethod()
          {
              return "TestClass";
          }
      
          public virtual string SealedMethod()
          {
              return "TestClass";
          }
      
          public abstract string AbstractMethod();
      }

       

      測試代碼如下:

      [TestClass]
      public class UnitTest
      {
          [TestMethod]
          public void TestMethod1()
          {
              var test = new Mock<TestClass>();
              test.Setup(t => t.NormalMethod()).Returns("Mock");
              test.Setup(t => t.VirtualMethod()).Returns("Mock");
              test.Setup(t => t.SealedMethod()).Returns("Mock");
              test.Setup(t => t.AbstractMethod()).Returns("Mock");
      
              Assert.AreEqual(test.Object.NormalMethod(), "Mock");
              Assert.AreEqual(test.Object.VirtualMethod(), "Mock");
              Assert.AreEqual(test.Object.SealedMethod(), "Mock");
              Assert.AreEqual(test.Object.AbstractMethod(), "Mock");
          }
      }

       

      MSTest 運行結果如下:

       

      NUnit 運行結果如下:

       

      去掉這個工具后會報如下錯誤:

       

      注意事項

      不要看上面的步驟簡單,我在配置這個的時候走了很多彎路,這里也跟大家分享一下:

       

      一定要用 Moq 等 Mock 框架

      為什么一定要自動 Mock 框架?它的核心不就是繼承一個類嗎?

      因為這個工具是在代碼編譯后才去修改 IL 代碼的。也就是說,在編寫的時候,它依然是密封類或者是非虛方法。

      所以你如果自己去編寫的話,是無法編譯通過的。

      那自動 Mock 框架為何可以呢?

      因為這些框架是在運行的時候動態生成一個類去繼承需要 Mock 的類的。在運行的時候,這個類已經被修改過了,所以是不會出錯的。

       

      注意配置一下 MSTest

      我在一開始研究這個的時候,遇到了一個很糾結的問題。

      在我的 Demo 中它是可以的,但是到了真正的項目中,它卻一直出錯。

      后來研究后發現,在出錯的項目中, Test 的運行目錄不是在 bin/Debug 下,

      而是在 TestResults/dozer_DOZER-PC 2012-11-27 11_11_22/Out

      而且這個文件夾會在每次運行測試的時候創建一個新的。里面的 dll 并不是從 bin/Debug 中復制過去的,所以我工具修改后的 dll 沒有起到作用。

      可是為什么我的 Demo 中沒有這樣?后來發現后面一個項目啟用了測試部署功能,雖然不知道這個功能具體的用處,但是取消后出錯的項目也正常了!

      取消方法:測試 — 編輯測試設置 — 本地(另一個也要同樣配置) — 部署 — 取消啟用部署。

      注意!配置有兩份,要同時取消后才可以生效。

       

      所有測試框架都支持嗎?

      原則上,只要你有辦法在運行測試之前跑一下這個工具就可以支持所有的測試框架。

      從上面可以看到,MSTest 和 NUnit 的配置方法是完全一樣的。

      經過測試,我們公司的自動化部署、測試框架是可以支持這個的,別的環境可能需要一些修改和配置,難度并不是很大。

       

      最后

      項目地址:https://github.com/dozer47528/MockHelper

      最后,感謝老趙提供的思路!我這里其實只是具體實現一下。

      其實,這個是無奈之舉,大家最好還是老老實實地多用接口吧!

      posted @ 2012-11-27 23:12  Dozer  閱讀(3849)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 亚洲av专区一区| 五十路久久精品中文字幕| 成年美女黄网站色大片免费看| 久久人妻av无码中文专区| 成人av专区精品无码国产| 亚洲成人av在线高清| 亚洲色偷拍区另类无码专区| 在线观看特色大片免费网站| 色偷偷www.8888在线观看| 131mm少妇做爰视频| 97精品亚成在人线免视频| 人妻日韩精品中文字幕| 亚洲熟妇色自偷自拍另类| 亚洲国产片一区二区三区| 福利视频在线一区二区| 成人无遮挡裸免费视频在线观看| 久久精产国品一二三产品| 99在线精品国自产拍中文字幕| 日韩一区二区a片免费观看| 亚洲一区二区三级av| 午夜三级成人在线观看| 全部免费毛片在线播放| 亚洲精品一区二区三区婷婷月| 日本中文字幕有码在线视频| 久久ww精品w免费人成| 97超级碰碰碰久久久久| 18禁一区二区每日更新| 精品久久久久久国产| 婷婷六月天在线| 国产成人永久免费av在线| 精品无码一区在线观看| 99久久精品看国产一区| 公喝错春药让我高潮| 岳阳县| 国产美女久久久亚洲综合| 亚洲午夜性猛春交XXXX| 岛国中文字幕一区二区| 午夜男女爽爽影院在线| 午夜自产精品一区二区三区| 博客| 蜜芽久久人人超碰爱香蕉|