测试驱动开发:基于Jenkins+GoTest+HTML的持续化集成

目录

前言

一、项目框架

1.项目迭代

2.项目时序图

3.项目测试执行

二、项目具体实现

1.创建流水线

2.拉取代码

3.执行测试代码

4.生成测试报告

5.报告内容解读

6.数据统计

7.邮件通知

8.企业微信通知

三、项目遇到的问题

[1.go test -args](#1.go test -args)

[2.go test生成html格式的报告](#2.go test生成html格式的报告)

3.数据统计问题

4.相对路径问题

5.错误排查问题


前言

目前我们的项目体系流程不够完善,我们针对这一现象引入了"测试驱动开发"观念,在开发测试部署阶段可以节省一部分工作量,对于比较复杂的场景,也可以编写一些测试工具。我们都知道如果仅靠传统的手工测试(偏功能)会存在很多的漏洞,为了提高迭代效率,引入自动化测试、CI/CD,在项目测试阶段、预上线、上线等各个阶段都能快速通过上述手段发现问题,保障产品质量。


一、项目框架

在日常测试过程中,需要验证测试环境&线上环境API接口,为了更方便,研究了通过Jenkins构建自动化项目并生成HTML报告。接下来会详细介绍项目构建步骤和遇到的问题。

1.项目迭代

2.项目时序图

3.项目测试执行


二、项目具体实现

1.创建流水线

(1)新建任务

(2)选择流水线或者复制现有流水线任务

(3)配置流水线

(4)pipeline脚本的基本框架

Groovy 复制代码
#!groovy



pipeline {

    agent any



    environment {

        GO_BINARY = "go"

        TEST_REPORT_PATH = "test-report.xml"

    }

    stages {

        stage('checkout') {

            steps {

                sh"""

                    echo "steps one"

                """

            }

        }

        stage('unit-test') {

            steps {

                echo "step two"

            }

        }

        stage('api-test') {

            steps {

                sh """

                   ehco "step three"

                   """

            }

        }

    }

    post {

        always {

            echo "clean over..."

            echo "send email"

        }

        success {

            echo 'Build && Test Succeeded.'

        }

        failure {

            echo 'Build && Test Failured.'

        }

    }

}

对应在jenkins上的阶段视图:

2.拉取代码

Groovy 复制代码
repoURL = "git拉取代码地址"

rootPath = "/var/jenkins_work/workspace/pid-openapi-test-report"

repoPath = "${rootPath}/$BUILD_ID"

...

stages {

    stage('checkout') {

        steps {

            sh"""

                export PATH="${arcPath}:${goRoot}:${kubectlRoot}:${makeRoot}:$PATH"

                git clone --depth 1 ${repoURL} ${repoPath}

            """

        }

    }

    ...

}

3.执行测试代码

利用go test命令执行代码。执行go test会进行全代码编译的,会拉取所有的依赖,所以需要提前配置go环境变量。

go test运行指定模块、指定优先级的测试用例,eg:

Go 复制代码
go test -v ./test/storage/... '-run=^Test+/TestP0' -json

./test/storage/ storage在openapi-go项目中的代码目录

'-run=^Test+/TestP0' ^Test指定Test打头的suite,/TestP0指定该suite下的用例。这样可以将模块storage、用例名称TestP0参数化为MODULE_NAME、PRIORITY,并在jenkins上的参数化构建中进行赋值。

配置完成后go test可以写成这样了:

Go 复制代码
go test -v ./test/$MODULE_NAME/...   -run="^Test"+"/Test"+$PRIORITY

4.生成测试报告

安装go-test-report

Go 复制代码
go get github.com/vakenbolt/go-test-report/

执行生成html格式测试报告的命令,会在当前目录生成一个test_report.html

Go 复制代码
go test -v ./test/$MODULE_NAME/...   -run="^Test"+"/Test"+$PRIORITY   -json | go-test-report

jenkins发布报告的pipeline script:

Groovy 复制代码
stage('Report') {           

        steps {

            echo "report"

            publishHTML (target: [

            allowMissing: false,

            alwaysLinkToLastBuild: false,

            keepAll: true,

            reportDir: '$BUILD_ID/test-output',

            reportFiles: 'test_report.html',

            reportName: "HTML Report"

        ])

        }

    }

然后就可以在jenkins查看该报告了

5.报告内容解读

失败的用例是红色,通过的用例是绿色。失败日志需要关注assert部分的日志,包括报错行数、期望值与实际值的比较结果。

6.数据统计

在测试代码执行结果及报告都有了之后就可以统计自已需要的数据,然后放在邮件内容里进行发送。

先分析下html源文件的内容,找到自已想要的数据。

groovy自带解析html格式的库,但是不太好用。这里采用awk解析数据。

注:substr(s,p,n) 返回字符串s从p开始长度为n的部分

Groovy 复制代码
def genReportBody() {
    // 生成测试报告内容
    def testReport = readFile("$BUILD_ID/test-output/test_report.html")
    
    // 获取执行时间
    sh(script: 'pwd')
    def duration = sh(script: 'grep "Duration:" '+"$BUILD_ID/test-output/test_report.html"+' | awk \'{print substr($6,9,length($6)-17)}\'', returnStdout: true).trim()
    echo duration
    def runtime = duration.split("\\.")[0].trim()
    echo runtime

    // 获取总数量
    def total = sh(script: 'grep "Total:" '+"$BUILD_ID/test-output/test_report.html"+' | awk \'{print substr($5,9,length($5)-26)}\'', returnStdout: true).trim()

    // 获取通过率
    def passedCount = sh(script: 'grep "Passed:" '+"$BUILD_ID/test-output/test_report.html"+' | awk \'{print substr($5,9,length($5)-17)}\'', returnStdout: true).trim()
    def skippedCount = sh(script: 'grep "Skipped:" '+"$BUILD_ID/test-output/test_report.html"+' | awk \'{print substr($5,9,length($5)-17)}\'', returnStdout: true).trim()
    def failedCount = sh(script: 'grep "Failed:" '+"$BUILD_ID/test-output/test_report.html"+' | awk \'{print substr($5,9,length($5)-17)}\'', returnStdout: true).trim()
    def passedRate = String.format("%.2f", passedCount.toInteger()/(total.toInteger()-skippedCount.toInteger()) * 100)

7.邮件通知

组装邮件中的内容

Groovy 复制代码
 // 生成测试报告
    def reportContent = """
        <h2>OpenAPI Test Report (${MODULE_NAME})</h2>
        <p>Environment: ${ENV}</p>
        <p>Test Time: ${runtime}s</p>
        <h3>Test Cases:</h3>
        <ul>
            <a href="https://jenkins地址/view/pid/job/${JOB_NAME}/$BUILD_ID/HTML_20Report/" target="_blank">https://jenkins地址/view/pid/job/${JOB_NAME}/$BUILD_ID/HTML_20Report/</a>
            
        </ul>
        <p>Pass Rate: ${passedRate}% </p>
        <p>Test Range: ${PRIORITY}</p>
        <h3>Failures: ${failedCount}</h3>
    """

发送邮件,在发送邮件前将无用的测试数据清除

post {

        always {

            sh """

                mv ${repoPath}/test-output ~/temp

                rm -rf ${repoPath}/*

                mv ~/temp/test-output ${repoPath}/

            """

            echo "clean over..."

            emailext body:  genReportBody(),

                    subject: 'Test Report',

                    // to: 'env.RECIPIENTS',

                    to: '${RECIPIENT_LIST}',

                    mimeType: 'text/html'

            // from: '邮件发送地址'

        }

        success {

            echo 'Build && Test Succeeded.'

        }

        failure {

            echo 'Build && Test Failured.'

        }

}

8.企业微信通知


三、项目遇到的问题

1.go test -args

利用该命令自定义参数时发现-args后面所有东西都当成agrs的值,且阻断后面所有指令的执行。后来在stackoverflow看见一个人发了同样的问题,我想到去看下官方说明

Groovy 复制代码
In addition to the build flags, the flags handled by 'go test' itself are:



-args

    Pass the remainder of the command line (everything after -args)

    to the test binary, uninterpreted and unchanged.

    Because this flag consumes the remainder of the command line,

    the package list (if present) must appear before this flag.

上面的everything after -args和执行实际效果是一样。这样通过命令行方式来切换环境的做法是行不通,于是采用多个配置文件的方式,全部存放在jenkins机器的~/conf目录。

切换方式

Groovy 复制代码
if [ $ENV = "test" ]

then

    echo 'cp test .env'

    cp /home/jenkins/conf/.env ${repoPath}/test

    cp /home/jenkins/conf/.env.storage ${repoPath}/test/storage/v1/.env

elif [ $ENV = "dev" ]

then

    #statements

    echo 'cp dev .env'

    cp /home/jenkins/conf/.env.dev ${repoPath}/test/.env

    cp /home/jenkins/conf/.env.storage.dev ${repoPath}/test/storage/v1/.env

    

elif [ $ENV = "prod" ]

then

    echo 'cp prod .env'

    cp /home/jenkins/conf/.env.prod ${repoPath}/test/.env

    cp /home/jenkins/conf/.env.storage.prod ${repoPath}/test/storage/v1/.env

fi

2.go test生成html格式的报告

最开始也是打算接入allure报告,但是发现go test并不支持,所以采用了go-test-report。发布的第一版的go test report时并不是长这样的

而是像下面这样不带css样式的

解决方法:在jenkins-->系统管理-->脚本命令行,输入以下命令

Groovy 复制代码
System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")

3.数据统计问题

网上有很多groovy统计xml格式的文件,没找到能很好解析html格式的工具,想到awk这个工具。

Groovy 复制代码
def passedCount = sh(script: 'grep "Passed:" '+"$BUILD_ID/test-output/test_report.html"+' | awk \'{print substr($5,9,length($5)-17)}\'', returnStdout: true).trim()

4.相对路径问题

我们用IDE编写用例时直接就可以执行了,这种情况下go会把该用例所在的目录当成pwd目录;而流水线中go test是在项目根目录下执行的,这时go是把项目根目录当成pwd目录的。这样用例中使相对路径eg:.env、../.env等都会执行失败。

解决方法:利用runtime获取当前执行路径,然后代码中生成项目根目录,以该路径为基点再去拼接文件的路径,尽量不要使相对路径。

5.错误排查问题

后面发现现有的脚本case编写如果有一个报错,全部都是红色,找到报错点不是很方便,修改脚本case为:

Go 复制代码
func (s *JobBatchGetSuite) TestP0_Normal() {
	s.Run("TestSuccessJobBatchGet", func() {
		s.TestSuccessJobBatchGet()
	})
}

func (s *JobBatchGetSuite) TestP1_Normal() {
	s.Run("TestJobBatchGetJobIdsTooMuch", func() {
		s.TestJobBatchGetJobIdsTooMuch()
	})
}

在jenkins上执行后,报告展示更为直观。

今天的分享就到此结束喽~

相关推荐
0思必得09 分钟前
[Web自动化] JS基础语法与数据类型
前端·javascript·自动化·html·web自动化
研华嵌入式15 分钟前
Ubuntu 20.04 停止支持怎么办?
linux·运维·ubuntu
野熊佩骑34 分钟前
一文读懂运维监控之 Ubuntu22.04安装部署Zabbix监控
linux·运维·服务器·网络·ubuntu·zabbix·database
双层吉士憨包1 小时前
Claude账号共享教程分享
运维·服务器
lllsure1 小时前
Linux 日志管理
linux·运维·服务器
工业HMI实战笔记1 小时前
HMI “卡成 PPT” 怎么办?—— 性能优化指南
ui·性能优化·自动化·交互
yewq-cn2 小时前
自动更新 Docker 镜像
运维·docker·容器
haluhalu.2 小时前
Linux系统下进程池设计与实现详解
linux·运维·服务器
m0_537473492 小时前
Nginx 生产环境平滑升级实战:从 1.24.0 到 1.28.0 的零宕机操作全记录
运维·nginx