1. 程式人生 > >好代碼是管出來的——.Net Core中的單元測試與代碼覆蓋率

好代碼是管出來的——.Net Core中的單元測試與代碼覆蓋率

情況 其它 netcore output 窗口 一個數據庫 過濾 and 令行

  測試對於軟件來說,是保證其質量的一個重要過程,而測試又分為很多種,單元測試、集成測試、系統測試、壓力測試等等,不同的測試的測試粒度和測試目標也不同,如單元測試關註每一行代碼,集成測試關註的是多個模塊是否能正常的協同工作。
  當我們在衡量代碼好壞時,其中一點就是這些代碼是否進行了單元測試,測試的質量、代碼覆蓋率怎麽樣?本文將從以下幾個方面介紹.Net Core中的單元測試:

  • 單元測試簡介
  • .Net Core中的單元測試框架
  • 使用xUnit.Net對.Net Core應用進行單元測試
    • 創建xUnit.Net測試項目
    • 編寫測試方法
    • 斷言
    • 運行單元測試
  • Mock
  • 單元測試代碼覆蓋率
  • 小結

單元測試簡介

  單元測試是指對軟件中的最小可測試單元進行檢查驗證,而.Net中最小可測試單元就是類和方法,單元測試是白盒測試關註於代碼執行邏輯,所以單元測試代碼一般也由開發人員編寫。
  單元測試關註的兩個重點就是最小可測試單元和代碼邏輯,但是很多情況下,一個類或者是方法它會依賴一些外部組件,如其他開發人員寫的代碼、第三方類庫、數據庫、網絡等,當被測試的代碼與這些組件緊耦合時,那麽這段代碼將可能是不可測的,如一個方法中依賴一個數據庫組件去訪問數據庫,那麽在執行這個方法時,必然要與數據庫交互,如果沒有數據庫,那麽該方法就無法運行。

  所以單元測試不僅是對代碼邏輯進行檢查,同時還對整個代碼結構有所限制,面向對象編程時應當遵循“依賴倒置”原則,模塊應該依賴抽象,抽象不應該依賴實現。並且所依賴的抽象,應該顯示的通過構造或者方法參數進行暴露,讓組件的使用者對組件的依賴一目了然。
  而在單元測試時為了屏蔽這些抽象依賴,不同測試框架中提供了stub、mock、fake等方式對抽象進行模擬,以便於代碼能夠正常執行。

.Net Core中的單元測試框架

  .Net Core中常用的單元測試的框架有MSTest、NUnit和xUnit.net,它們的使用方法都非常相似,都是通過特性標記的方式聲明測試方法,然後在方法中使用斷言(Assertions)來判別方法執行結果是否達到預期。

  這三個框架中MSTest是與VS集成的,而NUnit和xUnit.net都加入了.Net基金會,下面兩個圖分別是三個框架特性和斷言的比較(內容來自:https://xunit.github.io/docs/comparisons):
  特性:

  技術分享圖片

  斷言(部分):

  技術分享圖片 

  三個框架自有優點,但xUnit.net使用更廣泛一些(許多開源項目都使用xUnit.net,包括ASP.NET Core MVC、EF Core等項目),支持.Net下的大部分平臺(.Net Fx、.Net Core、UWP、Xamarin),並且具有非常好的可拓展性。

使用xUnit.Net對.Net Core應用進行單元測試

  本文使用xUnit.Net框架來對.Net Core程序進行單元測。

創建xUnit.Net測試項目

  在解決方案中添加.Net Core的被測試項目以及xUnit測試項目:

  技術分享圖片

  目錄結構:

  技術分享圖片

  xUnit測試項目還提供了相應的代碼分析器來幫助編寫測試代碼:

  技術分享圖片

編寫測試方法

  在被測試的項目中添加一個計算器類型,並添加加法運算的方法:

  技術分享圖片

  在測試項目中添加Calulator方法的測試代碼:

  技術分享圖片

斷言

  在程序中,斷言指的是一個表達式語句執行時總是為真(True),它有助於代碼閱讀、調試、編譯和缺陷檢測,當斷言內表達式執行為True時,不會執行任何操作,當結果為false時將會輸出一些異常信息,下圖是.Net中System.Diagnositics命名空間下提供的代碼調試使用的斷言用法(在調試程序時,當參數x < y就會中斷並拋出異常信息):

  技術分享圖片

  更多參考:https://docs.microsoft.com/en-us/visualstudio/debugger/assertions-in-managed-code
  在單元測試中,測試方法也是使用斷言的方式來判別程序執行結果與預期結果是否相符:

  技術分享圖片

  xUnit.net中的斷言參考:https://github.com/xunit/assert.xunit

運行單元測試

  在VS中可以使用VS的測試窗口運行測試方法:

  技術分享圖片

  運行結果(測試通過):

  技術分享圖片

  運行結果(測試未通過):

  技術分享圖片

Mock

  在文章前面提到過,面向對象編程應該顯示的依賴抽象,單元測試時應該將屏蔽依賴的影響(無論是依賴還未實現,或者實現的依賴會阻礙代碼執行),為了滿足這一需求出現了Mock、Fake等方式,其原理就是創建一個"假"的"空"的依賴,並用其替代真實依賴,以確保代碼能夠運行。
  .Net中一個常用的Mock框架是Moq,本文將使用Moq來介紹如何對依賴進行模擬:
  1. 編寫需要依賴的代碼:

  技術分享圖片

  技術分享圖片

  上面代碼中UserManager依賴一個用戶的倉儲類型,該倉儲將會與數據庫交互。
  2. 為測試項目安裝Moq組件:

  技術分享圖片

  3. 編寫測試代碼:

  技術分享圖片

  上面代碼通過Moq組件Mock了一個IUserRepository的類型,並將其Add方法設置並返回true(註:設置方法時參數的數據要與調用時使用的一致),最後通過Mock的對象實例Object來創建UserManager實例。
  最後斷言當創建用戶時,年齡為負數則拋出FormatException。
  4. 運行測試:

  技術分享圖片

  測試成功。

單元測試代碼覆蓋率

  測試代碼覆蓋率是對單元測試的一種度量,可以用來衡量單元測試是否達標,一般將代碼測試目標定到80%-90%之間,為了保證代碼覆蓋率,在寫測試用例時就要從語句覆蓋、條件覆蓋、路徑覆蓋等方面進行充分考慮。
  而.Net Core中如何在測試時計算代碼覆蓋率呢?如果使用VS的企業版,那麽VS自帶了代碼覆蓋率分析工具:

  技術分享圖片 

  詳情參考:https://docs.microsoft.com/en-us/visualstudio/test/using-code-coverage-to-determine-how-much-code-is-being-tested
       https://github.com/Microsoft/vstest-docs/blob/master/docs/analyze.md#coverage
  註:VS集成了MSTest,所以代碼覆蓋分析工具對MSTest支持非常好,但對xUnit.Net的支持如何筆者未進行測試。

  對於xUnit.net來說,要分析測試代碼覆蓋率還可以通過“OpenCover”和“ReportGenerator”工具完成,下面就介紹如何通過這兩個工具完成代碼覆蓋率的分析:
  1. 下載並安裝OpenCover,在OpenCover的GitHub上下載最新release的zip包,並解壓縮到指定目錄下,並將OpenCover目錄添加到環境變量中:

  技術分享圖片

  地址:https://github.com/OpenCover/opencover/releases

  2. 通過命令行使用OpenCover來完成覆蓋率分析:
  OpenCover有許多參數,具體參考:https://github.com/OpenCover/opencover/wiki/Usage
  在本例中,僅需要指定目標程序是dotnet.exe,目標程序參數是test(註:.Net Core的測試功能實際上是用.Net Core的CLI命令 dotnet test完成的),另外指定輸出文件名,register參數用於註冊代碼分析器默認使用user即可,-filter參數用於過濾不需要分析覆蓋率的程序集和類型,-oldstyle是為了支持.Net Core程序添加的參數(詳見:https://github.com/OpenCover/opencover/issues/595)
  另外為了能夠滿足測試需要在相關項目文件中添加以下節點(詳見:https://github.com/Microsoft/vstest/issues/800):

<PropertyGroup>
    <DebugType>full</DebugType>
</PropertyGroup>

  最後在項目目錄下執行以下命令:
  OpenCover.Console.exe -target:"dotnet.exe" -targetargs:"test" -output:coverage.xml -register:user -filter:"+[*]* -[*Moq]* -[xunit*]*" -oldstyle

  技術分享圖片

  生成的結果: 

  4. 通過ReportGenerator生成可讀報表:
  下載地址:https://github.com/danielpalme/ReportGenerator/releases
  註:下載解壓後將ReprotGenerator的目錄添加到環境變量,以便使用。
  執行以下命令:
  ReportGenerator.exe "-reports:coverage.xml" "-targetdir:report"

  技術分享圖片

  生成內容:

  技術分享圖片

  打開index.htm文件:

  技術分享圖片

  5. 在項目中創建一個bat文件,用於保存代碼覆蓋率檢測和報表生成命令,便於使用:

  技術分享圖片

小結

  本文主要介紹了如何使用xUnit.net測試框架完成.Net Core程序的單元測試,以及通過Moq框架來模擬測試目標的相關依賴,避免了其它組件對測試代碼的影響。
  文章的最後介紹了如何使用開源工具OpenCover和ReportGenerator工具來實現.Net Core單元測試代碼覆蓋率分析,這種方案相對VS企業版自帶的工具使用上要麻煩一些,但好在工具都是開源的,對持續集成也有比較好的支持,所以不失為一種好的解決方案。
  單元測試僅能保證軟件的最小可執行單元是正確的,真正的軟件是由這些最小可執行單元組成的一個整體,單元的正確性無法保證整體的正確性,下篇文章將對.Net Core的集成測試進行介紹。

本文鏈接:https://www.cnblogs.com/selimsong/p/9263957.html

測試代碼:https://github.com/yqszt/xUnitTestDemo

參考:
  https://en.wikipedia.org/wiki/Unit_testing
  https://docs.microsoft.com/en-us/dotnet/core/testing/
  https://github.com/aspnet/Home/wiki/Engineering-guidelines#unit-tests-and-functional-tests
  http://asp.net-hacker.rocks/2017/03/31/unit-testing-with-dotnetcore.html
  https://stackoverflow.com/questions/261139/nunit-vs-mbunit-vs-mstest-vs-xunit-net
  http://blog.ploeh.dk/2010/04/26/WhyImmigratingfromMSTesttoxUnit.net/
  https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test?tabs=netcore21
  https://stackoverflow.com/questions/38425936/how-to-measure-code-coverage-in-asp-net-core-projects-in-visual-studio
  http://dotnetliberty.com/index.php/2016/02/22/moq-on-net-core/
  https://github.com/moq/moq4
  https://stackoverflow.com/questions/41384459/opencover-reports-missing-pdbs-when-pdbs-are-present-xunit-net-core
  https://github.com/OpenCover/opencover
  https://github.com/danielpalme/ReportGenerator

好代碼是管出來的——淺談.Net Core的代碼管理方法與落地(更新中...)

好代碼是管出來的——.Net Core中的單元測試與代碼覆蓋率