.Net Core

自动化测试---.netCore 集成jenkins进行单元测试

2020-05-03  本文已影响0人  上岸的魚

.Net常用单元测试框架MsUnit/XUnit/NUnit,目前主流使用前两种较多,通常开发者会在Vs中使用自带单元测试方式进行单元测试验证,这种模式一旦需要全量重新跑单元测试时会花费较长时间,而且需要开发者每修改一部分代码就要手动执行一遍,效率较低下,同时也不利于整体统一质量管理;
本文我们将通过Jenkins集成自动完成.NetCore单元测试,并提取单元测试相关指标(如分支覆盖率,行覆盖率),将指标数据推送到我们自己的管理端进行开发质量管理 。
本文使用了MsUnit进行单元测试,您也可以换成XUint同样支持

1.准备项目

项目结构
visual studio单元测试

以下代码写的是模拟用户注册的功能(16岁后方可注册),没可参考价值。

业务代码:
 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}");
        }
    }
   单元测试脚本验证,同一个函数的不同单元测试返回验证方式尽量使用同样的语义,如我们对AddUser返回结果,均使用AssertIsTrue进行验证。

2.服务器环境准备

步骤1:coverlet.console安装
https://www.nuget.org/packages/coverlet.console/
下载最新版本包,目前是1.7.1
上传到服务器目录中(离线安装需要上传,如果服务器有网则不需要)
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拉代码到服务器目录中,然后到代码所在目录,我们执行如下脚本:
步骤1、2也可以开发阶段由开发人员添加好相关包,由于不容易约束,因而我们采用命令统一后期添加。
步骤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:执行单元测试并输出覆盖率

[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,可以看到产生了三个文件

[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/目录中,后续我们将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

上述步骤完成后,我们再次查看生成目录中的文件,发现生成了一些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.实现Jenkins自动化

上述过程我们是通过手动脚本模式验证该过程,现我们计划将整个过程自动化掉
编写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("#############################################开始请求单元测试结果##################################################")
              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("#############################################单元测试未启用,跳过单元测试##################################################")
          }
     }

执行脚本验证

Jenkins输出
我们到服务器上查看,相关html已生成。

4.部署Nginx查看生成报告

部署一个nginx,将其目录指向 /root/unit_test/

chmod 777 /root/unit_test/
制作一个nginx的Yaml文件,内容如下:
#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 #创建nginx pod
然后我们访问该项目的报告页面:


覆盖率报告
覆盖率报告

5.结语

通过该模式我们实现了.netCore程序单元的自动化,并生成相关报告。
后续我们还可以扩展功能,提取相关报告数据用于对发布过程进行跟踪与管控,进一步规范开发及部署过程。

上一篇下一篇

猜你喜欢

热点阅读