自動化測試---.netCore 集成jenkins進行單元測試

.Net常用單元測試框架MsUnit/XUnit/NUnit,目前主流使用前兩種較多,通常開發(fā)者會在Vs中使用自帶單元測試方式進行單元測試驗證,這種模式一旦需要全量重新跑單元測試時會花費較長時間,而且需要開發(fā)者每修改一部分代碼就要手動執(zhí)行一遍,效率較低下,同時也不利于整體統(tǒng)一質(zhì)量管理;
本文我們將通過Jenkins集成自動完成.NetCore單元測試,并提取單元測試相關(guān)指標(biāo)(如分支覆蓋率,行覆蓋率),將指標(biāo)數(shù)據(jù)推送到我們自己的管理端進行開發(fā)質(zhì)量管理 。
本文使用了MsUnit進行單元測試,您也可以換成XUint同樣支持

1.準(zhǔn)備項目

項目結(jié)構(gòu)

visual studio單元測試

以下代碼寫的是模擬用戶注冊的功能(16歲后方可注冊),沒可參考價值。

業(yè)務(wù)代碼:
 public class BLLUserService
    {
        public int AddUser(string userId, string userName, int age)
        {
            if (string.IsNullOrEmpty(userId)) return 0;                 
            if (age < 5) return -2; //為測試多做了一些場景
            if (age < 10) return -1; //為測試多做了一些場景
            if (age < 16) return 0;
            return 1;
        }
    }
單元測試代碼參考:
public class BLLUserServiceTests
    {
        [TestMethod()]
        public void AddUserTest0()
        {
            BLLUserService service = new BLLUserService();
            int result = service.AddUser("zhangsan", "張三", 20);
            Assert.IsTrue(result == 1);          
        }
        [TestMethod()]
        public void AddUserTest1()
        {
            BLLUserService service = new BLLUserService();          
            int result = service.AddUser("zhangsan", "張三", 15);
            Assert.IsTrue(result == 0);
            result = service.AddUser("zhangsan", "張三", 16);
            Assert.IsTrue(result == 1);
        }
        [TestMethod()]
        public void AddUserTest2()
        {
            BLLUserService service = new BLLUserService();
            int result = service.AddUser("", "張三", 20);
            Assert.IsTrue(result == 0);  
        }
        [TestMethod()]
        public void AddUserTest3()
        {
            BLLUserService service = new BLLUserService();
            int result = service.AddUser("", "", 20);
            Assert.IsTrue(result == 0);         
        }
        [TestMethod()]
        public void AddUserTest4()
        {
            BLLUserService service = new BLLUserService();
            int result = service.AddUser("zhangsan", "", 20);
            Assert.IsTrue(result == 1);
        }
        [DataTestMethod]
        [DataRow(15)]
        [DataRow(10)]
        [DataRow(5)]
        public void IsPrime_01(int value)
        {
            BLLUserService service = new BLLUserService();
            int result = service.AddUser("zhangsan", "aa", value);  
            Assert.IsTrue(result<=0, $"{value}");
        }
    }
   單元測試腳本驗證,同一個函數(shù)的不同單元測試返回驗證方式盡量使用同樣的語義,如我們對AddUser返回結(jié)果,均使用AssertIsTrue進行驗證。

2.服務(wù)器環(huán)境準(zhǔn)備

步驟1:coverlet.console安裝
https://www.nuget.org/packages/coverlet.console/
下載最新版本包,目前是1.7.1
上傳到服務(wù)器目錄中(離線安裝需要上傳,如果服務(wù)器有網(wǎng)則不需要)
dotnet tool install --global coverlet.console --version 1.7.1
步驟2:安裝完畢后 vim /etc/profile 添加 /root/.dotnet/tools到Path目錄

/etc/profile添加如下一行即可。
export PATH=$PATH:/root/.dotnet/tools
保存
source /etc/profile 使修改生效

手動驗證
git拉代碼到服務(wù)器目錄中,然后到代碼所在目錄,我們執(zhí)行如下腳本:
步驟1、2也可以開發(fā)階段由開發(fā)人員添加好相關(guān)包,由于不容易約束,因而我們采用命令統(tǒng)一后期添加。
步驟1:項目中添加coverlet.msbuild -v 2.8.1包
coverlet.msbuild可以為我們輸出單元測試覆蓋

[root@k8s-master netCore02]# ls
build  docker  netCore02.Service  netCore02.sln  netCore02.UTest  README.md  unit_test
[root@k8s-master netCore02]# /root/jenkins/tools/dotnetsdk3.1/dotnet add netCore02.UTest/ package coverlet.msbuild -v 2.8.1
  Writing /tmp/tmptRfH2t.tmp
info : Adding PackageReference for package 'coverlet.msbuild' into project '/root/jenkins/workspace/netCore02/netCore02.UTest/netCore02.UTest.csproj'.
info : Restoring packages for /root/jenkins/workspace/netCore02/netCore02.UTest/netCore02.UTest.csproj...
info : Package 'coverlet.msbuild' is compatible with all the specified frameworks in project '/root/jenkins/workspace/netCore02/netCore02.UTest/netCore02.UTest.csproj'.
info : PackageReference for package 'coverlet.msbuild' version '2.8.1' updated in file '/root/jenkins/workspace/netCore02/netCore02.UTest/netCore02.UTest.csproj'.
info : Committing restore...
info : Assets file has not changed. Skipping assets file writing. Path: /root/jenkins/workspace/netCore02/netCore02.UTest/obj/project.assets.json
log  : Restore completed in 459.72 ms for /root/jenkins/workspace/netCore02/netCore02.UTest/netCore02.UTest.csproj.

步驟2:在項目中添加ReportGenerator -v 4.5.6包
使用ReportGenerator 可以輸出比較友好的報告,不是必選

[root@k8s-master netCore02]# /root/jenkins/tools/dotnetsdk3.1/dotnet add netCore02.UTest/ package ReportGenerator -v 4.5.6
  Writing /tmp/tmpVocxxx.tmp
info : Adding PackageReference for package 'ReportGenerator' into project '/root/jenkins/workspace/netCore02/netCore02.UTest/netCore02.UTest.csproj'.
info : Restoring packages for /root/jenkins/workspace/netCore02/netCore02.UTest/netCore02.UTest.csproj...
info : Package 'ReportGenerator' is compatible with all the specified frameworks in project '/root/jenkins/workspace/netCore02/netCore02.UTest/netCore02.UTest.csproj'.
info : PackageReference for package 'ReportGenerator' version '4.5.6' updated in file '/root/jenkins/workspace/netCore02/netCore02.UTest/netCore02.UTest.csproj'.
info : Committing restore...
info : Assets file has not changed. Skipping assets file writing. Path: /root/jenkins/workspace/netCore02/netCore02.UTest/obj/project.assets.json
log  : Restore completed in 394.22 ms for /root/jenkins/workspace/netCore02/netCore02.UTest/netCore02.UTest.csproj.
[root@k8s-master netCore02]

步驟3:Build項目

[root@k8s-master netCore02]# /root/jenkins/tools/dotnetsdk3.1/dotnet build
Microsoft (R) Build Engine version 16.4.0+e901037fe for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Restore completed in 29.62 ms for /root/jenkins/workspace/netCore02/netCore02.Service/netCore02.Service.csproj.
  Restore completed in 34.73 ms for /root/jenkins/workspace/netCore02/netCore02.UTest/netCore02.UTest.csproj.
  netCore02.Service -> /root/jenkins/workspace/netCore02/netCore02.Service/bin/Debug/netcoreapp3.1/netCore02.Service.dll
  netCore02.UTest -> /root/jenkins/workspace/netCore02/netCore02.UTest/bin/Debug/netcoreapp3.1/netCore02.UTest.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:02.30

步驟4:執(zhí)行單元測試并輸出覆蓋率

[root@k8s-master netCore02]# /root/jenkins/tools/dotnetsdk3.1/dotnet test . /p:CollectCoverage=true '/p:CoverletOutputFormat="lcov,opencover"' /p:CoverletOutput=/root/unit_test/netCore02/ --logger 'trx;LogFileName=/root/unit_test/netCore02/result.xml' /p:failOnError=true /p:keepLongStdio=true
Test run for /root/jenkins/workspace/netCore02/netCore02.UTest/bin/Debug/netcoreapp3.1/netCore02.UTest.dll(.NETCoreApp,Version=v3.1)
Microsoft (R) Test Execution Command Line Tool Version 16.3.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...

A total of 1 test files matched the specified pattern.
Results File: /root/unit_test/netCore02/result.xml
                                                                                                                                                                                    
Test Run Successful.
Total tests: 9
     Passed: 9
 Total time: 1.4794 Seconds

Calculating coverage result...
  Generating report '/root/unit_test/netCore02/coverage.info'
  Generating report '/root/unit_test/netCore02/coverage.opencover.xml'

+-------------------+--------+--------+--------+
| Module            | Line   | Branch | Method |
+-------------------+--------+--------+--------+
| netCore02.Service | 11.11% | 60%    | 7.69%  |
+-------------------+--------+--------+--------+

+---------+--------+--------+--------+
|         | Line   | Branch | Method |
+---------+--------+--------+--------+
| Total   | 11.11% | 60%    | 7.69%  |
+---------+--------+--------+--------+
| Average | 11.11% | 60%    | 7.69%  |
+---------+--------+--------+--------+

此時查看我們目錄 /root/unit_test/netCore02,可以看到產(chǎn)生了三個文件

[root@k8s-master netCore02]# ls
coverage.info  coverage.opencover.xml  result.xml
其中我們可以從coverage.opencover.xml獲取單元測試覆蓋率

說明:CoverletOutputFormat目前支持這幾種格式:
json (default)
lcov
opencover
cobertura
步驟5:生成單元測試報告

#生成在/root/unit_test/netCore02/目錄中,后續(xù)我們將unit_test通過nginx暴露出來,即可直接查看報告了。
[root@k8s-master netCore02]# /root/jenkins/tools/dotnetsdk3.1/dotnet  /root/.nuget/packages/reportgenerator/4.5.6/tools/netcoreapp3.0/ReportGenerator.dll -reports:/root/unit_test/netCore02/coverage.opencover.xml -targetdir:/root/unit_test/netCore02/
2020-05-03T14:52:18: Arguments
2020-05-03T14:52:18:  -reports:/root/unit_test/netCore02/coverage.opencover.xml
2020-05-03T14:52:18:  -targetdir:/root/unit_test/netCore02/
2020-05-03T14:52:18: Executable: /root/.nuget/packages/reportgenerator/4.5.6/tools/netcoreapp3.0/ReportGenerator.Core.dll
2020-05-03T14:52:18: Working directory: /root/jenkins/workspace/netCore02
2020-05-03T14:52:18: Writing report file '/root/unit_test/netCore02/index.htm'
2020-05-03T14:52:18: Report generation took 0.2 seconds

上述步驟完成后,我們再次查看生成目錄中的文件,發(fā)現(xiàn)生成了一些Html/js/css文件,這些文件是格式化后的可以直接查看的報告頁面。

[root@k8s-master netCore02]# ls /root/unit_test/netCore02
class.js                  icon_fork.svg          icon_search-plus.svg    icon_wrench.svg                       netCore02.Service_Program.htm
coverage.info             icon_info-circled.svg  icon_sponsor.svg        index.htm                             netCore02.Service_Startup.htm
coverage.opencover.xml    icon_minus.svg         icon_star.svg           main.js                               netCore02.Service_WeatherForecast.htm
icon_cube.svg             icon_plus.svg          icon_up-dir_active.svg  netCore02.Service_BLLUserService.htm  report.css
icon_down-dir_active.svg  icon_search-minus.svg  icon_up-dir.svg         netCore02.Service_HomeController.htm  result.xml

3.實現(xiàn)Jenkins自動化

上述過程我們是通過手動腳本模式驗證該過程,現(xiàn)我們計劃將整個過程自動化掉
編寫Jenkins腳本

        stage('Unit Test') {
          if (unitTest?.trim()) {
             println("#############################################開始單元測試##################################################")
             withEnv(["DOTNET_ROOT=/root/jenkins/tools/dotnetsdk3.0"]) {
               sh(script: '/root/jenkins/tools/' + sdkVersion+ '/dotnet add ' + unitTest + '  package coverlet.msbuild -v 2.8.1',returnStdout: true )
               sh(script: '/root/jenkins/tools/' + sdkVersion+ '/dotnet add '+ unitTest +  '  package ReportGenerator -v 4.5.6',returnStdout: true )
               sh(script: '/root/jenkins/tools/'+sdkVersion+'/dotnet build')
               def consoleLog=   sh(script: '/root/jenkins/tools/'+sdkVersion+ '/dotnet test .  /p:CollectCoverage=true  /p:CoverletOutputFormat=\\"lcov,opencover\\"  /p:CoverletOutput=\"${WORKSPACE}/unit_test/\"    --logger "trx;LogFileName=${WORKSPACE}/unit_test/result.xml"  /p:failOnError=true  /p:keepLongStdio=true',returnStdout: true )
               println("#############################################單元測試完畢##################################################")              
               println(consoleLog)              
               println("#############################################開始請求單元測試結(jié)果##################################################")
              def temp_out
              temp_out=sh(script:"ls ./unit_test/coverage.opencover.xml",returnStatus:true)
              println(temp_out)
              if(temp_out==0)
              {
                  sh(script: '/root/jenkins/tools/'+sdkVersion+ '/dotnet   /root/.nuget/packages/reportgenerator/4.5.6/tools/netcoreapp3.0/ReportGenerator.dll -reports::${WORKSPACE}/unit_test/coverage.opencover.xml   -targetdir:/root/unit_test/'+service+'/',returnStdout: true )                                
              }
              else{
                 println("##################################coverage.opencover.xml不存在#############################");
                 sh "exit 1"
              }              
             }
          }
           else {
                    println("#############################################單元測試未啟用,跳過單元測試##################################################")
          }
     }

執(zhí)行腳本驗證

Jenkins輸出

我們到服務(wù)器上查看,相關(guān)html已生成。

4.部署Nginx查看生成報告

部署一個nginx,將其目錄指向 /root/unit_test/

chmod 777 /root/unit_test/
制作一個nginx的Yaml文件,內(nèi)容如下:
#deploy
apiVersion: apps/v1
kind: Deployment
metadata:
  name: unit-nginx
  namespace: my-system
spec:
  selector:
    matchLabels:
      app: unit-nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: unit-nginx
    spec:
      nodeSelector:
        kubernetes.io/hostname: k8s-master
      containers:
      - name: unit-nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: html
        - mountPath: /etc/nginx/nginx.conf
          name: conf
        - mountPath: /etc/nginx/conf.d
          name: confd
      volumes:
      - name: html
        hostPath:
          path: /root/unit_test
      - name: conf
        hostPath:
          path: /root/unit_test/nginx.conf
      - name: confd
        hostPath:
          path: /root/unit_test/conf.d
---
#service
apiVersion: v1
kind: Service
metadata:
  name: unit-nginx
  namespace: my-system
spec:
  #type: NodePort
  ports:
  - port: 3080
    protocol: TCP
    targetPort: 80
    #nodePort: 31680
  selector:
    app: unit-nginx

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
   name: unit-nginx
   namespace: my-system
spec:
   rules:
   - host: u.xxx.cn
     http:
       paths:
       - path: /
         backend:
          serviceName: unit-nginx
          servicePort: 3080

kubectl apply -f unit_nginx.yaml #創(chuàng)建nginx pod
然后我們訪問該項目的報告頁面:


覆蓋率報告

覆蓋率報告

5.結(jié)語

通過該模式我們實現(xiàn)了.netCore程序單元的自動化,并生成相關(guān)報告。
后續(xù)我們還可以擴展功能,提取相關(guān)報告數(shù)據(jù)用于對發(fā)布過程進行跟蹤與管控,進一步規(guī)范開發(fā)及部署過程。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,283評論 6 530
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 97,947評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,094評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,485評論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,268評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,817評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,906評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,039評論 0 285
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,551評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,502評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,662評論 1 366
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,188評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 43,907評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,304評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,563評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,255評論 3 389
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,637評論 2 370