【5min+】為你的.NET應用進行一次全方位體檢
系列介紹
【五分鐘的dotnet】是一個利用您的碎片化時間來學習和豐富.net知識的博文系列。它所包含了.net體系中可能會涉及到的方方面面,比如C#的小細節,AspnetCore,微服務中的.net知識等等。
通過本篇文章您將Get:
- 為
.NET Core
應用添加單元測試 - 為
.NET Core
應用進行代碼覆蓋率度量 - 使用
Azure Devops
進行自動化構建 - 收穫類似於下面的這些徽章:
時長為大約有十分鐘,內容豐富,建議先投幣再上車觀看😜
正文
發現網上很少有講解關於.NET Core
的單元測試
文章,代碼覆蓋率
的文章就更少了。所以就抽時間梳理了一篇😁。
單元測試
先來說一下單元測試
,對於已經開始使用單元測試的小夥伴可以直接跳過這個小節。
那麼咱們為什麼需要進行單元測試呢?肯定是為了減少錯誤和bug的發生呀,這個不用說大家都知道。網上也有很多介紹單元測試
的文章,但是大多都是從一個很簡單的方法入手,比如下面這個方法:
public int SimpleMehtod(int a1, int a2)
{
return a1 + a2;
}
然後告訴大家我們需要對這個方法進行驗證。 其實這種教程由潛入深,好是好,但是很多沒有涉及過單元測試的小夥伴就會感到很懵逼:「這個代碼這麼簡單,我為啥要單元測試?一眼就看出返回兩個值的和」,這樣反而不能更好的體現單元測試所帶來的直觀好處。
所以,咱們反過來,思考有下面的一個方法:
/// <summary>
/// 獲取某個類型的泛型參數
/// </summary>
/// <param name="type">需要檢測的類型</param>
/// <param name="genericType">檢測類型所繼承的泛型接口</param>
/// <returns>泛型接口的所有參數信息</returns>
public Type[] MyDemoMethod(Type type, Type genericType)
{
return type.GetInterfaces()
.Where(i => IsGenericType(i))
.SelectMany(i => i.GetGenericArguments())
.ToArray();
bool IsGenericType(Type type1)
=> type1.IsGenericType && type1.GetGenericTypeDefinition() == genericType;
}
相對而言該方法就顯得複雜一些,它的功能是返回一個類型所繼承的泛型接口的所有參數。
假設我們在一次功能迭代中,編寫了這樣一個MyDemoMethod
的方法,該方法很明顯是作為一個工具方法來被其它調用者使用。那麼,當我們剛剛編寫完這個方法的時候,我們就很想知道這個方法是不是能夠正確的執行怎麼辦呢?「編寫一個控制台程序來測試?」、「等最後功能全部寫完了再來看」、「不管了」。
在咱們沒有使用單元測試的時候,上面的幾個操作是常見的情況,可能很多小夥伴會基於控制台來測試;還有一些小夥伴直接F5運行應用來進行測試,這樣直接運行程序會花費我們大量的瑣碎時間(比如登錄,操作功能,進入模塊,測試該功能…………);最後是那些「等最後功能全部寫完了再來看」的朋友,如果記性還不錯的話,最後還可以記得來要對這個功能測試,要是後期編寫了很多其它業務功能的話,可能早都忘記有這個方法了,所以最後就是完全「裸奔」。
所以,此時就需要咱們引入「單元測試」了。當一個方法被多個地方使用,過早的對該方法進行單元測試
,將會大幅度的減少bug的產生。
在.NET Core
中使用單元測試也很簡單,直接新建一個測試項目就可以了。本次文章選擇的是基於Xunit
所建立的測試項目,然後在測試項目中引用需要測試的項目:
編寫測試用例
接下來您需要對您需要測試的類編寫對應的測試用例。假如我們編寫了如下的方法(別問我為什麼不是上面的那個泛型基礎方法,因為待會要測代碼覆蓋率,為了簡單):
public int CalDemo(int s, bool checkSign = true)
{
if (s > 10 && checkSign)
{
return s << 2;
}
else
{
return s;
}
}
在實際項目中,我們可能有許許多多像這樣的方法。具有幾個分支,每個分支執行不同的代碼。針對該CalDemo
方法,很明顯當傳入參數s
大於10和小於10的時候有着不同的執行邏輯(先忽略checkSign
參數),所以我們可以分別測試當s大於10或者s小於等於10的情況:
在xunit
測試項目中編寫以下用例:
[Fact]
public void CalDemo_ArguementMoreThan10()
{
var testValue = 11;
DemoClass demoClass = new DemoClass();
var result = demoClass.CalDemo(testValue);
//判斷結果是否等於 44。 如果是則測試通過
Assert.Equal(44, result);
}
[Fact]
public void CalDemo_ArguementLessThan10()
{
var testValue = 9;
DemoClass demoClass = new DemoClass();
var result = demoClass.CalDemo(testValue);
//判斷結果是否等於 9。 如果是則測試通過
Assert.Equal(9, result);
}
這樣我們就完成了對該方法的測試,當然您還可以編寫:「傳入參數等於10」,「傳入參數為空」,「傳入參數為負數」等等用例。
在VS
中打開”測試資源管理器”來運行測試看看吧:
有關xunit
的使用,您可以參考:Getting Started with xUnit.net
代碼覆蓋率
通過「測試資源管理器」,我們可以看到單元測試的正確與否。但是,我如何知道該單元的代碼是否都測試完成了呢?如果沒有完成我還需要編寫哪些測試用例呢?
這個時候,我們就需要對測試進行度量,度量哪些代碼已經被我們測試過,哪些代碼沒有被測試到。針對沒有測試到的部分,我們再編寫一些Case進行測試。
所以我們可以引入代碼覆蓋率
的概念來進行評估。關於該概念的內容我這裡就不在過多闡述了,大家有興趣可以「百度谷歌必應」三條龍服務。
在VS
中,為我們提供了代碼覆蓋率的菜單項:在「測試」 菜單中,選擇「分析所有測試的代碼覆蓋率」 。
通過該功能我們就可以對已有的單元測試進行代碼覆蓋率度量。
是不是很簡單? 但是你會發現:「你根本找不到這個按鈕!!!!!!」。
別找了,您的Visual Studio 2019
沒得這個菜單? 為什麼呢? 因為您沒有充錢啊!!!
,該功能只針對Visual Studio Enterprise
(企業版)提供。使用社區版
的我,眼淚流下來。
你以為這樣就能難倒我了嗎? 直接上開源的度量工具:coverlet。來看看關於Coverlet的介紹:「Coverlet是一個跨平台的.NET代碼覆蓋框架,支持行、分支和方法覆蓋。它與Windows上的.NET Framework和所有受支持的平台上的.NET Core一起工作。」
這裡我強烈推薦大家使用Coverlet來進行代碼覆蓋率測試,為什麼呢?因為它跨平台呀。後面我們會使用Linux環境來進行自動化構建,所以Coverlet具有明顯的優勢,在Azure
的官方文檔中也推薦大家使用Coverlet:
使用Coverlet
使用Coverlet
也很簡單,直接在您的測試項目安裝對應的Nuget
包依賴就可以了:
dotnet add package coverlet.collector
因為跨平台的特性,所以您可能已經想到了,咱們接下來就沒有像「測試資源管理器」那樣的界面可以一鍵點擊了,所以我們得使用命令行的方式來進行操作,對於一些小夥伴可能需要習慣習慣。
在xunit
項目中執行以下命令:
dotnet test --collect:"XPlat Code Coverage"
我個人比較喜歡用powershell
來執行,當然您可以在vs中用程序包管理控制台來選中項目執行:
執行後您會發現在項目中多了一個叫做TestResults
的文件夾,該文件夾就是本次代碼度量的結果:
度量報告
但是您馬上又會發現一個問題,這個報告它喵的是xml格式,看起來十分費解。
所以,我們又引入了另外一個神器:ReportGenerator
。關於該工具的描述可以參考:ReportGenerator。 它的作用就像它的名字一樣,就是為了生成代碼覆蓋的報告。
ReportGenerator提供了幾種使用方式,一種是您通過Nuget
包來使用它,還有就是把他作為一個全局的命令行指令工具來安裝它。 這裡我們選擇了第二種,為了以後使用,所以選全局的來的爽。
在powershell
中執行下面命令:
dotnet tool install --global dotnet-reportgenerator-globaltool
然後就可以使用它來生成報告了,還是用powershell
在xunit
測試項目中執行下面的代碼:
reportgenerator "-reports:**\coverage.cobertura.xml" "-targetdir:coveragereport"
這句話的意思是:根據將xunit
項目下的coverage.cobertura.xml
文件來生成報告,輸出目錄為coveragereport
。
執行之後,在目錄中就會出現一個名為coveragereport
的文件夾,打開以後點擊裏面的Index文件打開網頁。
哇,效果舒服多了:
這裡您會看到有兩個度量指標:一個叫做Line coverage
(語句覆蓋),另一個叫做Branch coverage
(分支覆蓋率)。然後您可以點擊咱們的源代碼文件進入,看看為什麼會有這樣的結果:
紅色的部分就是咱們已經覆蓋的語句,直觀的就能看到我們測試了哪些代碼。而左側箭頭所標記的地方就是具有分支的地方,這個s > 10 && checkSing
就是一個明顯的分支。
通過這種方式我們就能夠清楚並且直觀的知道我們的代碼哪些完成了測試,哪些地方有遺漏等。
單元測試 + 代碼覆蓋率 的方式能夠大幅度的減少我們開發中隱藏的bug,特別是作為個人開發者來說,因為沒有專門的測試人員,所以需要自己檢測自己的代碼,純靠肉眼來觀察的話是很粗糙的,畢竟自己寫的代碼自己最難發現bug。因此假如時間允許,我們應該儘可能的引入單元測試
和代碼覆蓋率
。
一般來說,編寫單元測試會擴大代碼量至3倍以上,所以這也是很多公司或者開發者選擇放棄使用單元測試
的原因。但是「出來混遲早是要還的」,假如是一個長期運行的項目,越早發現bug是越關鍵的一件事,這將關係到項目後期能否穩定運行下去。
注意!!!,哪怕代碼覆蓋率達到了100%,也不是證明項目就不會出現bug了。單元測試的全覆蓋只能證明您的單元沒有問題,需求理解錯誤或者功能集成時所導致的bug是不會在該階段被發現的,因此我們還是需要進行其它的測試,比如集成測試,自動化接口測試等。
Azure Devops
既然有了這麼好的單元測試
和代碼覆蓋率
,那我肯定希望每次提交代碼的時候就能夠為這次的代碼進行一次測試和反饋。所以咱們可以使用微軟這些年吹爆了的Azure
平台,人人上雲,雲上兩開花。
接下來,將展示如何利用Azure Devops
進行自動化構建。在這之前,咱們先來看看微軟為我們開發者帶來的一些福利:
關於自動化構建,您也可以選用Github Action
。大家都知道,自從Github
被納入到微軟旗下之後,勢頭也是越發的猛,現在Github teams
都直接免費了。再來看看Azure Devops
這邊,假如是開源項目,直接免費使用,就算是私有,每個都有30個小時的使用時間。這兩兄弟雙管齊下,爾等小菜只能說一句「微軟巨硬牛B」。
每一次自動化構建的Job背後都是使用雲服務器的資源來進行構建,微軟直接在Github和Azure這邊提供了免費的資源來供您構建,配置好像還是一台2核4G的主機。用老羅的話來說,真的是「不賺錢,就交個朋友」。
所以要使用Azure Devops
的話,請先註冊您的微軟賬號。下面的演示我將代碼託管在Github上,權限為公開,然後從Azure Devops
這邊鏈接Github的庫進行構建。
從Pipelines
中新建一個Pipelines:
我這裡選擇的是Github
的代碼庫,然後下一步進行選擇,選擇項會有幾個模板供您選擇,您可以隨意選擇一個AspNet Core
的模板,然後進行下一步進行配置。在下面的圖片中,表示了一個對.NET Core
程序進行「自動生成->測試->生成代碼覆蓋率」的job,您可以根據您的自身情況進行參考和更改:
然後提交該配置。當master
分支的代碼進行變動的時候,job就會自動執行,執行的結果可以在Pipelines
看到:
再來看看咱們的代碼度量結果:
完美。
徽章收集
不知道有沒有人像一樣,很喜歡點QQ圖標之類的東西。(所以我在博客園添加了兩個徽章😂)
當然,使用徽章的話可以讓用戶一下就了解到項目的情況,比如版本號,下載數量,開源協議等等。
Azure Piplines
提供了一個徽章,您可以從job的右上角獲取到:
該徽章是關於job的構建成功的信息。但是如果您想獲取到其它的信息,可以使用shields來進行獲取:
打開shields的官網:「//shields.io/category/downloads」。 選擇您所需要添加的徽章類別,這裡咱們選擇了Azure Coverage
:
進行輸入對應信息後,就可以獲取到剛才咱們job中所得到的代碼覆蓋率的結果了。
然後…………選則一些您需要展示的信息,很快就累計了一排勳章了😂。
最後
說幾個大家可能在單元測試過程中可能涉及到的幾個小點:
- 有時候您會測試一個
internal
級別的類,但是當測試項目引用之後是沒有辦法找到該類的,您可以通過將程序集標記為對測試項目可見來進行測試:
[assembly: InternalsVisibleTo("MyDemo.Tests")]
namespace DuDuDu.MyDemo.Internals
{
internal class DefaultDemoClass
{
}
}
2.如果有依賴注入怎麼辦? 比如咱們測試AspNetCore的應用時,會有很多類其實是被注入到了DI容器中,但是測試的類又依賴了這些類。
可以使用ServiceCollection
來作為測試的容器實現,然後把涉及的服務都添加進去:
[Fact]
public void Test()
{
IServiceCollection Services = new ServiceCollection();
Services.AddTransient<xx,xx>();
Services.AddTransient<xx,xx>();
Services.AddTransient<xx,xx>();
var provider = Services.BuildServiceProvider();
var testClass = provider.GetService<xx>();
testClass.TestMethod();
}
如果有依賴的類還沒有實現的時候,可以通過Mock的方法來模擬一個接口完成操作。
3.如果項目多了的話,怎麼執行測試和代碼度量呢?
我現在選用的是使用Powershell
腳本來編寫腳本完成的。開發的時候利用VS的「測試資源管理器」來進行單元測試,當單元測試驗證的差不多的時候,使用「Powershell」腳本來進行代碼覆蓋率進行測試,查看忽略的代碼然後繼續測試。測試通過之後再提交代碼到Github
,然後Azure Devops
進行構建。
好啦,今天的內容有些多,但是對您開發.NET Core
項目來說的話,是實實在在的有用。
預告一下,下一期會為大家帶來「對AspNet Core返回結果進行自動包裝」的文章😄。
最後,偷偷說一句:創作不易,點個推薦吧…..