測試對於軟件來講,是保證其質量的一個重要過程,而測試又分爲不少種,單元測試、集成測試、系統測試、壓力測試等等,不一樣的測試的測試粒度和測試目標也不一樣,如單元測試關注每一行代碼,集成測試關注的是多個模塊是否能正常的協同工做。
當咱們在衡量代碼好壞時,其中一點就是這些代碼是否進行了單元測試,測試的質量、代碼覆蓋率怎麼樣?本文將從如下幾個方面介紹.Net Core中的單元測試:php
單元測試是指對軟件中的最小可測試單元進行檢查和驗證,而.Net中最小可測試單元就是類和方法,單元測試是白盒測試關注於代碼執行邏輯,因此單元測試代碼通常也由開發人員編寫。
單元測試關注的兩個重點就是最小可測試單元和代碼邏輯,可是不少狀況下,一個類或者是方法它會依賴一些外部組件,如其餘開發人員寫的代碼、第三方類庫、數據庫、網絡等,當被測試的代碼與這些組件緊耦合時,那麼這段代碼將多是不可測的,如一個方法中依賴一個數據庫組件去訪問數據庫,那麼在執行這個方法時,必然要與數據庫交互,若是沒有數據庫,那麼該方法就沒法運行。
因此單元測試不只是對代碼邏輯進行檢查,同時還對整個代碼結構有所限制,面向對象編程時應當遵循「依賴倒置」原則,模塊應該依賴抽象,抽象不該該依賴實現。而且所依賴的抽象,應該顯示的經過構造或者方法參數進行暴露,讓組件的使用者對組件的依賴一目瞭然。
而在單元測試時爲了屏蔽這些抽象依賴,不一樣測試框架中提供了stub、mock、fake等方式對抽象進行模擬,以便於代碼可以正常執行。html
.Net Core中經常使用的單元測試的框架有MSTest、NUnit和xUnit.net,它們的使用方法都很是類似,都是經過特性標記的方式聲明測試方法,而後在方法中使用斷言(Assertions)來判別方法執行結果是否達到預期。
這三個框架中MSTest是與VS集成的,而NUnit和xUnit.net都加入了.Net基金會,下面兩個圖分別是三個框架特性和斷言的比較(內容來自:https://xunit.github.io/docs/comparisons):
特性:git
斷言(部分):github
三個框架自有優勢,但xUnit.net使用更普遍一些(許多開源項目都使用xUnit.net,包括ASP.NET Core MVC、EF Core等項目),支持.Net下的大部分平臺(.Net Fx、.Net Core、UWP、Xamarin),而且具備很是好的可拓展性。數據庫
本文使用xUnit.Net框架來對.Net Core程序進行單元測。編程
在解決方案中添加.Net Core的被測試項目以及xUnit測試項目:網絡
目錄結構:框架
xUnit測試項目還提供了相應的代碼分析器來幫助編寫測試代碼:asp.net
在被測試的項目中添加一個計算器類型,並添加加法運算的方法:ide
在測試項目中添加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、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的集成測試進行介紹。
本文連接:http://www.javashuo.com/article/p-njyztdms-du.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