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

news2024/11/27 10:19:40

目录

前言        

一、项目框架

1.项目迭代

2.项目时序图

3.项目测试执行

二、项目具体实现

1.创建流水线

2.拉取代码

3.执行测试代码

4.生成测试报告

5.报告内容解读

6.数据统计

7.邮件通知

8.企业微信通知

三、项目遇到的问题

1.go test -args 

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

3.数据统计问题

4.相对路径问题

5.错误排查问题


前言        

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

     


一、项目框架

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

1.项目迭代

2.项目时序图

3.项目测试执行


二、项目具体实现

1.创建流水线

(1)新建任务

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

(3)配置流水线

(4)pipeline脚本的基本框架

#!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.拉取代码

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 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 test -v ./test/$MODULE_NAME/...   -run="^Test"+"/Test"+$PRIORITY

4.生成测试报告

安装go-test-report

go get github.com/vakenbolt/go-test-report/

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

go test -v ./test/$MODULE_NAME/...   -run="^Test"+"/Test"+$PRIORITY   -json | go-test-report

jenkins发布报告的pipeline script:

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的部分

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.邮件通知

组装邮件中的内容

 // 生成测试报告
    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看见一个人发了同样的问题,我想到去看下官方说明

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目录。

切换方式

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-->系统管理-->脚本命令行,输入以下命令

System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")

3.数据统计问题

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

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为:

func (s *JobBatchGetSuite) TestP0_Normal() {
	s.Run("TestSuccessJobBatchGet", func() {
		s.TestSuccessJobBatchGet()
	})
}

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1393066.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++中基类的析构函数为什么要用virtual虚析构函数

直接的讲&#xff0c;C中基类采用virtual虚析构函数是为了防止内存泄漏。具体地说&#xff0c;如果派生类中申请了堆内存空间&#xff0c;并在其析构函数中对这些内存空间进行释放。假设基类中采用的是非虚析构函数&#xff0c;当删除基类指针指向的派生类对象时就不会触发动态…

给科研人的 ML 开源发布工具包

什么是开源发布工具包&#xff1f; 恭喜你的论文成功发表&#xff0c;这是一个巨大的成就&#xff01;你的研究成果将为学界做出贡献。 其实除了发表论文之外&#xff0c;你还可以通过发布研究的其他部分&#xff0c;如代码、数据集、模型等&#xff0c;来增加研究的可见度和采…

tinyxml2

使用tinyxml2&#xff0c;得知道一些xml基础 xml tutorial--菜鸟 tinyxml2类对象 链接 结构 XMLNode 什么是节点 节点&#xff1a;元素、声明、文本、注释等。 XMLDocument xml文档(文件)对象。 作用&#xff1a; 加载xml文件&#xff0c; tinyxml2作用 1&#xff0c;…

数据守护盾牌:敏感数据扫描与脱敏,让安全合规无忧

前言 在信息时代&#xff0c;数据已经成为企业和组织的核心资产&#xff0c;其价值与日俱增。然而&#xff0c;随着数据使用的普及和复杂度的提升&#xff0c;数据安全与合规问题也变得越来越突出。敏感数据的保护显得尤为重要&#xff0c;因为这些数据一旦泄露或被不当使用&a…

Servlet系列两种创建方式

一、使用web.xml的方式配置&#xff08;Servlet2.5之前使用&#xff09; 在早期版本的Java EE中&#xff0c;可以使用XML配置文件来定义Servlet。在web.xml文件中&#xff0c;可以定义Servlet的名称、类名、初始化参数等。然后&#xff0c;在Java代码中实现Servlet接口&#x…

【机器学习300问】9、梯度下降是用来干嘛的?

当你和我一样对自己问出这个问题后&#xff0c;分析一下&#xff01;其实我首先得知道梯度下降是什么&#xff0c;也就它的定义。其次我得了解它具体用在什么地方&#xff0c;也就是使用场景。最后才是这个问题&#xff0c;梯度下降有什么用&#xff1f;怎么用&#xff1f; 所以…

muduo网络库剖析——监听者EpollPoller类

muduo网络库剖析——监听者EpollPoller类 前情从muduo到my_muduo 概要epoll原理解析epoll提供的接口epoll的触发模式epoll实现多路复用 框架与细节成员函数使用方法 源码结尾 前情 从muduo到my_muduo 作为一个宏大的、功能健全的muduo库&#xff0c;考虑的肯定是众多情况是否…

低代码配置-属性配置面板设计

模块设计 tab项切换 组件基础属性组件数据属性组件事件属性表单属性 模块输出函数设计 tab切换函数 列表表单属性 数据来源&#xff1a; 调用接口时一次赋予&#xff0c;无需使用selectItem&#xff0c;如需使用&#xff0c;归入基础属性列表标题是否展示筛选区域 示例&am…

前端框架前置学习Webpack(1) 常用webpack配置

什么是Webpack? 定义 本质上,Webpack是用于现代JavaScript应用程序的静态模块打包工具.当webpack处理应用程序时,它会在内部从一个或多个入口点构建一个依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个bundles,它们均为静态资源,用于展示你的内容.…

数学建模--论文

内容来自数学建模BOOM&#xff1a;【快速入门】北海&#xff1a;数模建模基础MATLAB入门论文写作数学模型与算法(推荐数模美赛国赛小白零基础必看教程)_哔哩哔哩_bilibili 目录 一、论文整体模版 1.整体框架 2.示例 二、标题 1.标题主题事项 三、摘要 1.摘要三要素&am…

LaTeX 多栏文档 Multiple columns如何插入图片并修改样式

在今天写报告的时候用到了 latex 的多栏列表&#xff0c;插入图片的时候感觉很无助 如果不喜欢让Latex自动安排图片位置&#xff0c;可以使用float包&#xff0c;然后可以使用\begin{figure}[H]。 记得提前导入这个包 \usepackage{float} 为了让我的图片的caption居中&#xf…

Go 语言中高效切片拼接和 GO 1.22 提供的新方法

Table Contents 切片拼接的必要性基本拼接方法及其局限性使用 append 函数高效拼接的策略控制容量和避免副作用利用 Go 1.22 的新特性切片动态扩容的深入理解内存重新分配与数据迁移性能优化策略结论在 Go 语言中,切片拼接是一项常见的操作,但如果处理不当,可能会导致性能问…

Verilog刷题笔记15

题目&#xff1a; An adder-subtractor can be built from an adder by optionally negating one of the inputs, which is equivalent to inverting the input then adding 1. The net result is a circuit that can do two operations: (a b 0) and (a ~b 1). See Wikipe…

[go语言]输入输出

目录 知识结构 输入 1.Scan ​编辑 2.Scanf 3.Scanln 4.os.Stdin --标准输入&#xff0c;从键盘输入 输出 1.Print 2.Printf 3.Println 知识结构 输入 为了展示集中输入的区别&#xff0c;将直接进行代码演示。 三者区别的结论&#xff1a;Scanf格式化输入&#x…

中科院罗小舟团队提出 UniKP 框架,大模型 + 机器学习高精度预测酶动力学参数

作者&#xff1a;李宝珠 编辑&#xff1a;三羊 中国科学院深圳先进技术研究院罗小舟团队提出了&#xff0c;基于酶动力学参数预测框架 (UniKP)&#xff0c;实现多种不同的酶动力学参数的预测。 众所周知&#xff0c;生物体内的新陈代谢是通过各种各样的化学反应来实现的。这…

SpringBoot 统计API接口用时该使用过滤器还是拦截器?

统计请求的处理时间&#xff08;用时&#xff09;既可以使用 Servlet 过滤器&#xff08;Filter&#xff09;&#xff0c;也可以使用 Spring 拦截器&#xff08;Interceptor&#xff09;。两者都可以在请求处理前后插入自定义逻辑&#xff0c;从而实现对请求响应时间的统计。 …

Modelsim SE 10.5安装教程

ModelSim 是一种功能强大的硬件描述语言 (HDL&#xff0c;Hardware Description Language) 仿真和验证工具&#xff0c;可以单独仿真&#xff0c;也可以联合Quartus/Vivado等软件联合仿真&#xff0c;仿真速度快&#xff0c;广泛应用于数字电路设计和验证领域。 大学老师爱教VH…

JavaWeb后端——Maven

maven主要服务于基于Java平台的项目构建、依赖管理和项目信息管理 maven项目对象模型简称POM&#xff0c; maven解决问题&#xff1a; 1. 添加第三方jar包&#xff0c;maven将 jar 包放在本地仓库中统一管理&#xff0c;使用时用坐标的方式引用即可 2. 解决 jar 包之间的依…

计算机网络-计算机网络的概念 功能 发展阶段 组成 分类

文章目录 计算机网络的概念 功能 发展阶段总览计算机网络的概念计算机网络的功能计算机网络的发展计算机网络的发展-第一阶段计算机网络的发展-第二阶段-第三阶段计算机网络的发展-第三阶段-多层次ISP结构 小结 计算机网络的组成与分类计算机网络的组成计算机网络的分类小结 计…

springBoot 添加自定义类库包

一、新建SpringBoot Web 二、添加类库包 com.saas.pdf 删除掉多余的类&#xff0c;新建类&#xff1a;PdfUtil.java package com.saas.pdf;public class PdfUtil {public static void Save(String filePath) {System.out.println("保存成功&#xff01;");} }三、…