【5min+】爲你的.NET應用進行一次全方位體檢

系列介紹

【五分鐘的dotnet】是一個利用您的碎片化時間來學習和豐富.net知識的博文系列。它所包含了.net體系中可能會涉及到的方方面面,好比C#的小細節,AspnetCore,微服務中的.net知識等等。git

經過本篇文章您將Get:github

  • .NET Core應用添加單元測試
  • .NET Core應用進行代碼覆蓋率度量
  • 使用Azure Devops進行自動化構建
  • 收穫相似於下面的這些徽章:
    x

時長爲大約有十分鐘,內容豐富,建議先投幣再上車觀看😜shell

正文

發現網上不多有講解關於.NET Core單元測試文章,代碼覆蓋率的文章就更少了。因此就抽時間梳理了一篇😁。服務器

單元測試

先來講一下單元測試,對於已經開始使用單元測試的小夥伴能夠直接跳過這個小節。框架

那麼我們爲何須要進行單元測試呢?確定是爲了減小錯誤和bug的發生呀,這個不用說你們都知道。網上也有不少介紹單元測試的文章,可是大多都是從一個很簡單的方法入手,好比下面這個方法:ide

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所創建的測試項目,而後在測試項目中引用須要測試的項目:

x

編寫測試用例

接下來您須要對您須要測試的類編寫對應的測試用例。假如咱們編寫了以下的方法(別問我爲何不是上面的那個泛型基礎方法,由於待會要測代碼覆蓋率,爲了簡單):

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中打開"測試資源管理器"來運行測試看看吧:

x

有關xunit的使用,您能夠參考:Getting Started with xUnit.net

代碼覆蓋率

經過「測試資源管理器」,咱們能夠看到單元測試的正確與否。可是,我如何知道該單元的代碼是否都測試完成了呢?若是沒有完成我還須要編寫哪些測試用例呢?

這個時候,咱們就須要對測試進行度量,度量哪些代碼已經被咱們測試過,哪些代碼沒有被測試到。針對沒有測試到的部分,咱們再編寫一些Case進行測試。

因此咱們能夠引入代碼覆蓋率的概念來進行評估。關於該概念的內容我這裏就不在過多闡述了,你們有興趣能夠「百度谷歌必應」三條龍服務。

VS中,爲咱們提供了代碼覆蓋率的菜單項:在「測試」 菜單中,選擇「分析全部測試的代碼覆蓋率」 。

x

經過該功能咱們就能夠對已有的單元測試進行代碼覆蓋率度量。

x

是否是很簡單? 可是你會發現:「你根本找不到這個按鈕!!!!!!」。

別找了,您的Visual Studio 2019沒得這個菜單? 爲何呢? 由於您沒有充錢啊!!!,該功能只針對Visual Studio Enterprise(企業版)提供。使用社區版的我,眼淚流下來。

你覺得這樣就能難倒我了嗎? 直接上開源的度量工具:coverlet。來看看關於Coverlet的介紹:「Coverlet是一個跨平臺的.NET代碼覆蓋框架,支持行、分支和方法覆蓋。它與Windows上的.NET Framework和全部受支持的平臺上的.NET Core一塊兒工做。」

這裏我強烈推薦你們使用Coverlet來進行代碼覆蓋率測試,爲何呢?由於它跨平臺呀。後面咱們會使用Linux環境來進行自動化構建,因此Coverlet具備明顯的優點,在Azure的官方文檔中也推薦你們使用Coverlet:

x

使用Coverlet

使用Coverlet也很簡單,直接在您的測試項目安裝對應的Nuget包依賴就能夠了:

dotnet add package coverlet.collector

由於跨平臺的特性,因此您可能已經想到了,我們接下來就沒有像「測試資源管理器」那樣的界面能夠一鍵點擊了,因此咱們得使用命令行的方式來進行操做,對於一些小夥伴可能須要習慣習慣。

xunit項目中執行如下命令:

dotnet test --collect:"XPlat Code Coverage"

我我的比較喜歡用powershell來執行,固然您能夠在vs中用程序包管理控制檯來選中項目執行:

x

執行後您會發如今項目中多了一個叫作TestResults的文件夾,該文件夾就是本次代碼度量的結果:

x

度量報告

可是您立刻又會發現一個問題,這個報告它喵的是xml格式,看起來十分費解。

因此,咱們又引入了另一個神器:ReportGenerator。關於該工具的描述能夠參考:ReportGenerator。 它的做用就像它的名字同樣,就是爲了生成代碼覆蓋的報告。

ReportGenerator提供了幾種使用方式,一種是您經過Nuget包來使用它,還有就是把他做爲一個全局的命令行指令工具來安裝它。 這裏咱們選擇了第二種,爲了之後使用,因此選全局的來的爽。

powershell中執行下面命令:

dotnet tool install --global dotnet-reportgenerator-globaltool

而後就可使用它來生成報告了,仍是用powershellxunit測試項目中執行下面的代碼:

reportgenerator "-reports:**\coverage.cobertura.xml" "-targetdir:coveragereport"

這句話的意思是:根據將xunit項目下的coverage.cobertura.xml文件來生成報告,輸出目錄爲coveragereport

x

執行以後,在目錄中就會出現一個名爲coveragereport的文件夾,打開之後點擊裏面的Index文件打開網頁。

哇,效果舒服多了:

x

這裏您會看到有兩個度量指標:一個叫作Line coverage(語句覆蓋),另外一個叫作Branch coverage(分支覆蓋率)。而後您能夠點擊我們的源代碼文件進入,看看爲何會有這樣的結果:

x

紅色的部分就是我們已經覆蓋的語句,直觀的就能看到咱們測試了哪些代碼。而左側箭頭所標記的地方就是具備分支的地方,這個s > 10 && checkSing就是一個明顯的分支。

經過這種方式咱們就可以清楚而且直觀的知道咱們的代碼哪些完成了測試,哪些地方有遺漏等。

單元測試 + 代碼覆蓋率 的方式可以大幅度的減小咱們開發中隱藏的bug,特別是做爲我的開發者來講,由於沒有專門的測試人員,因此須要本身檢測本身的代碼,純靠肉眼來觀察的話是很粗糙的,畢竟本身寫的代碼本身最難發現bug。所以假如時間容許,咱們應該儘量的引入單元測試代碼覆蓋率

通常來講,編寫單元測試會擴大代碼量至3倍以上,因此這也是不少公司或者開發者選擇放棄使用單元測試的緣由。可是「出來混早晚是要還的」,假如是一個長期運行的項目,越早發現bug是越關鍵的一件事,這將關係到項目後期可否穩定運行下去。

注意!!!,哪怕代碼覆蓋率達到了100%,也不是證實項目就不會出現bug了。單元測試的全覆蓋只能證實您的單元沒有問題,需求理解錯誤或者功能集成時所致使的bug是不會在該階段被發現的,所以咱們仍是須要進行其它的測試,好比集成測試,自動化接口測試等。

Azure Devops

既然有了這麼好的單元測試代碼覆蓋率,那我確定但願每次提交代碼的時候就可以爲此次的代碼進行一次測試和反饋。因此我們可使用微軟這些年吹爆了的Azure平臺,人人上雲,雲上兩開花。

接下來,將展現如何利用Azure Devops進行自動化構建。在這以前,我們先來看看微軟爲咱們開發者帶來的一些福利:

x
x

關於自動化構建,您也能夠選用Github Action。你們都知道,自從Github被歸入到微軟旗下以後,勢頭也是愈加的猛,如今Github teams都直接免費了。再來看看Azure Devops這邊,假如是開源項目,直接無償使用,就算是私有,每一個都有30個小時的使用時間。這兩兄弟左右開弓,爾等小菜只能說一句「微軟巨硬牛B」。

每一次自動化構建的Job背後都是使用雲服務器的資源來進行構建,微軟直接在Github和Azure這邊提供了免費的資源來供您構建,配置好像仍是一臺2核4G的主機。用老羅的話來講,真的是「不賺錢,就交個朋友」。

因此要使用Azure Devops的話,請先註冊您的微軟帳號。下面的演示我將代碼託管在Github上,權限爲公開,而後從Azure Devops這邊連接Github的庫進行構建。

Pipelines中新建一個Pipelines:

Pipelines

x

我這裏選擇的是Github的代碼庫,而後下一步進行選擇,選擇項會有幾個模板供您選擇,您能夠隨意選擇一個AspNet Core的模板,而後進行下一步進行配置。在下面的圖片中,表示了一個對.NET Core程序進行「自動生成->測試->生成代碼覆蓋率」的job,您能夠根據您的自身狀況進行參考和更改:

x
x

而後提交該配置。當master分支的代碼進行變更的時候,job就會自動執行,執行的結果能夠在Pipelines看到:

x

再來看看我們的代碼度量結果:
x

完美。

徽章收集

不知道有沒有人像同樣,很喜歡點QQ圖標之類的東西。(因此我在博客園添加了兩個徽章😂)

固然,使用徽章的話可讓用戶一下就瞭解到項目的狀況,好比版本號,下載數量,開源協議等等。

x

Azure Piplines提供了一個徽章,您能夠從job的右上角獲取到:

x

該徽章是關於job的構建成功的信息。可是若是您想獲取到其它的信息,可使用shields來進行獲取:

打開shields的官網:「https://shields.io/category/downloads」。 選擇您所須要添加的徽章類別,這裏我們選擇了Azure Coverage:

進行輸入對應信息後,就能夠獲取到剛纔我們job中所獲得的代碼覆蓋率的結果了。

而後…………選則一些您須要展現的信息,很快就累計了一排勳章了😂。

最後

說幾個你們可能在單元測試過程當中可能涉及到的幾個小點:

  1. 有時候您會測試一個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返回結果進行自動包裝」的文章😄。

最後,偷偷說一句:創做不易,點個推薦吧.....

x

相關文章
相關標籤/搜索