1. VisualStuio中的測試資源管理器、CodeLens和ReSharper "上一篇文章" 重溫了《單元測試的藝術》里提到的單元測試的技術及原則。這篇文章實踐使用VisualStudio 2019進行單元測試。 在VisualStudio中通常都會使用“測試資源管理器”進行單元測試。 Pr ...
1. VisualStuio中的測試資源管理器、CodeLens和ReSharper
上一篇文章重溫了《單元測試的藝術》里提到的單元測試的技術及原則。這篇文章實踐使用VisualStudio 2019進行單元測試。
在VisualStudio中通常都會使用“測試資源管理器”進行單元測試。
Professional和Enterprise版本可以使用CodeLens,這大大方便了測試的運行與調試。
但CodeLens的圖標常常刷不出來,一些第三方插件(如ReSharper)會更好用。
2. Live Unit Test
VisualStudio可以使用Live Unit Test(實時單元測試),這個功能需要Enterprise版本。
Live Unit Testing 是 Visual Studio 2017 中引入的一種技術。 進行代碼更改時,它會自動執行單元測試。
實時單元測試:
讓你更有信心地對代碼進行重構和更改。 Live Unit Testing 在編輯代碼時自動執行所有受影響的測試,確保所做更改不會中斷測試。
指示單元測試是否充分覆蓋代碼,並顯示未被單元測試覆蓋的代碼。 Live Unit Testing 以圖形方式實時描繪代碼覆蓋率,以便一眼就能看到每行代碼覆蓋的測試數,目和未被任何單元測試覆蓋的行。
Live Unit Testing是個很好的功能,唯一的障礙是,如果解決方案中包含了集成測試會導致Live Unit Testing響應變慢。解決方案是創建一個不包含集成測試項目的解決方案,或者在解決方案資源管理器中右鍵單擊想要排除的每個測試項目,然後依次選擇“實時測試” > “排除”,這樣Live Unit Test就不會對這些項目進行測試。
3. 代碼覆蓋率
還是Enterprise版本的功能,Visual Studio的代碼覆蓋率工具可以很直觀地查看到單元測試的代碼覆蓋率。
4. Microsoft Fakes
微軟有他自己的隔離框架Microsoft Fakes(在公司名稱後面加Fakes,這命名真是超爛)。不過Fakes不怎麼建議使用。
Fakes有兩種風格:
Stub(存根) 將類替換為可實現同一介面的小型替代項。
Shim(填充碼) 在運行時修改應用的編譯代碼,這樣就可以運行測試提供的墊片代碼,而不用執行指定的方法調用。 填充碼可用於替換對無法修改的程式集(如 .NET 程式集)的調用。
一般原則是,為在 Visual Studio 解決方案中進行的調用使用存根,併為對其他引用的程式集的調用使用填充碼。 這是因為在你自己的解決方案中,通過按照存根要求的方式定義介面來分離組件是一個很好的做法。 但是,外部程式集(如 System.dll)通常沒有單獨的介面定義,因此必須改用填充碼。
其他需要註意的事項還有:
性能。 填充碼運行較慢,因為它們在運行時會重新編寫你的代碼。 存根沒有這項性能開銷,與虛方法運行的速度一樣快。
靜態方法和密封類型方法。 你只能使用存根實現介面。 因此,存根類型不能用於靜態方法、非虛方法、密封虛方法、密封類型中的方法,等等。
內部類型。 存根和填充碼都可用於可通過程式集特性 InternalsVisibleToAttribute 訪問的內部類型。
私有方法。 如果方法簽名中的所有類型都是可見的,則填充碼可替換對私有方法的調用。 存根只能替換可見方法。
介面和抽象方法。 存根提供了可用於測試的介面和抽象方法的實現。 填充碼無法檢測介面和抽象方法,因為它們沒有方法體。
但是由於不建議使用Fakes,所以基本上都會用NSub創建偽對象,Fakes的價值在於Shim,它有些別的隔離框架沒有的獨特功能。
下麵已LogAn項目為例講解Fakes的用法。首先在單元測試的引用列表右鍵選中LogAn
項目,選擇“添加 Fakes 程式集”,然後重新生成方案,這時候可見到項目中多了LogAn.Fakes
的引用,以及多了一些Fakes的文件。
使用Stub的單元測試代碼如下:
ICalculator calculator = new Fakes.StubICalculator
{
AddInt32Int32 = (arg1, arg2) => 3
};
Assert.AreEqual(calculator.Add(1, 2), 3);
重溫一下NSub的相同功能:
_calculator = Substitute.For<ICalculator>();
_calculator.Add(1, 2).Returns(3);
Assert.AreEqual(_calculator.Add(1, 2), 3);
和NSub不同,Fakes提供的功能少了很多,不僅如此,每次更改項目都可能要重新添加Fakes引用(至少我在編譯伺服器上的項目老是因為Fakes出錯)。所以一般不建議使用Fakes做Stub的功能。但是官方文檔中外部程式集(如 System.dll)通常沒有單獨的介面定義,因此必須改用填充碼 這句話卻沒有錯,反正System.dll之類的第三方程式集又不可能經常改變,所以也沒有需要重新添加Fakes程式集這個煩惱。下麵介紹一下Shim的使用。
假設有一個類:
public static class Y2KChecker
{
public static void Check()
{
if (DateTime.Now == new DateTime(2000, 1, 1))
throw new ApplicationException("y2kbug!");
}
}
由於它依賴於DateTime.Now,而假設我們沒辦法更改這段代碼,為了對它進行單元測試我們必須使用Shim破除對DateTime.Now的依賴。首先選中System
引用並右鍵選擇添加Fake程式集
,然後在測試代碼的ShimsContext中插入Shim:
using (ShimsContext.Create())
{
// Arrange:
System.Fakes.ShimDateTime.NowGet = () => new DateTime(2000, 1, 1);
// Act and Assert::
Assert.ThrowsException<ApplicationException>(Y2KChecker.Check);
}
如上面代碼所示,Shim可以偽造DateTime.Now的值,這對單元測試提供了極大的方便。
5. 結語
雖然Fakes中的Stub不好用,但Shim還是挺有趣的,我建可以同時使用NSub和Fakes里的Shim。善用VisualStudio的各種工具可以大大提升單元測試的效率,不過基本上這些工具都只在Enterprise版本中提供。
有趣的是MSTest自己也沒有用Fakes,很多時候都是用moq。(例如PlatformServiceProviderTests.cs)
另外VisualStudio還有一些有趣的工具輔助單元測試,例如walterlv的這篇博客(不再為命名而苦惱!使用 MSTestEnhancer 單元測試擴展,寫契約就夠了)介紹了一種更直觀的單元測試編寫方式,不放試一試:
6. 參考
測試工具 - Visual Studio Microsoft Docs
單元測試 - Visual Studio Microsoft Docs
用 Microsoft Fakes 隔離測試代碼 - Visual Studio Microsoft Docs
瞭解如何使用實時單元測試測試代碼 - Visual Studio Microsoft Docs
代碼覆蓋率測試 - Visual Studio Microsoft Docs