zl程序教程

您现在的位置是:首页 >  后端

当前栏目

自动化测试实战:0基础→自动化测试框架→企业级自动化持续集成

2023-09-11 14:14:24 时间

01 为什么要写企业级持续集成(jenkins + pipeline + k8s)?

目前网上自动化持续集成的资料很多,但基本上都是局限于jenkins自由风格的job,结合shell脚本来实现持续集成,这种方式的缺点也很明显:

构建出问题,排查困难
构建节点挂了,就不能完成构建任务

而当前主流技术是 “ k8s + 微服务 ” 等,我们完全可以利用k8s的优势来完成持续构建任务,每次构建时可以调度到任意节点,或者是具有指定标签的节点,这就实现了高可用,另外,结合pipeline,可以轻松简单实现持续集成,并且哪个阶段出问题了一目了然,排查起来也容易。

02 自动化测试框架

0基础到实现:java + testng + httpclient + allure
参考:https://www.cnblogs.com/uncleyong/p/15867903.html

03 企业级持续集成技术栈

git + gitlab + jenkins + pipeline + maven + harbor + docker + k8s

可以整合python、java等各种自动化测试框架

流程:拉取代码–》mvn打包–》构建镜像–》新镜像发布到k8s–》拉取自动化测试代码–》自动化测试–》allure报告

环境:jenkins使用k8s作为构建环境,在某个节点执行测试

在这里插入图片描述

04 环境规划

_
192.168.117.160:harbor、jenkins、maven
192.168.117.161:k8s-master
192.168.117.162:k8s-node01
192.168.117.163:k8s-node02
192.168.117.180:gitlab
每台虚拟机都安装了git

05 环境搭建

git安装及使用:https://www.cnblogs.com/uncleyong/p/10854115.html
gitlab安装及使用:https://www.cnblogs.com/uncleyong/p/16557785.html
maven安装及使用:https://www.cnblogs.com/uncleyong/p/10743181.html
docker安装及使用:https://www.cnblogs.com/uncleyong/p/8894133.html
harbor安装以及使用:https://www.cnblogs.com/uncleyong/p/15469575.html
k8s安装:https://www.cnblogs.com/uncleyong/p/15499732.html(k8s操作:https://www.cnblogs.com/uncleyong/p/15499743.html)
jenkins搭建及devops自动化平台相关配置:https://www.cnblogs.com/uncleyong/p/16555667.html
allure报告:allure-commandline下载、安装、配置(linux或者docker),https://www.cnblogs.com/uncleyong/p/16726826.html
jenkins把自动化测试结果发送到钉钉群:https://www.cnblogs.com/uncleyong/p/16724590.html

06 pipeline设计

pipeline常用功能:https://www.cnblogs.com/uncleyong/p/16705620.html

使用Blue Ocean设计pipeline脚本:https://www.cnblogs.com/uncleyong/p/16727971.html

说明:由于资源不足,暂未加入sonarqube等

图片

07 pipeline具体实现

结合上面在Blue Ocean中设计的pipeline骨架,我们来完善并实现整个过程

parameters

可以选择要构建的分支

parameters {
gitParameter branch: '', branchFilter: 'origin/(.*)', defaultValue: '', description: '构建的分支', name: 'BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'GitParameterDefinition'
}

环境变量

environment {
  HARBOR_ADDRESS = "192.168.117.160"
  IMAGE_NAME = "gift"
  NAMESPACE = "gift"
}

agent

k8s作为构建环境

  agent {
    kubernetes {
      cloud 'qzcsbj_kubernetes'
      yaml '''apiVersion: v1
kind: Pod
spec:
  containers:
    - image: 'registry.cn-chengdu.aliyuncs.com/qzcsbj6/jnlp:alpine'     
      name: jnlp
      imagePullPolicy: IfNotPresent
      args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "localtime"
          readOnly: false
    - image: "registry.cn-chengdu.aliyuncs.com/qzcsbj6/kubectl"
      imagePullPolicy: "IfNotPresent"
      name: "kubectl"
      tty: true
      command:
        - "cat"
      env:
        - name: "LANGUAGE"
          value: "en_US:en"
        - name: "LANG"
          value: "en_US.UTF-8"
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "localtime"
          readOnly: false
    - image: "registry.cn-chengdu.aliyuncs.com/qzcsbj6/docker"
      imagePullPolicy: "IfNotPresent"
      name: "docker"
      tty: true
      command:
        - "cat"
      env:
        - name: "LANGUAGE"
          value: "en_US:en"
        - name: "LANG"
          value: "en_US.UTF-8"
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "localtime"
          readOnly: false
        - mountPath: "/var/run/docker.sock"
          name: "dockersock"
          readOnly: false
    - image: "registry.cn-chengdu.aliyuncs.com/qzcsbj6/maven"
      imagePullPolicy: "IfNotPresent"
      name: "maven"
      tty: true
      command:
        - "cat"
      env:
        - name: "LANGUAGE"
          value: "en_US:en"
        - name: "LANG"
          value: "en_US.UTF-8"
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "localtime"
        - mountPath: "/root/.m2/"
          name: "m2dir"
          readOnly: false
    - image: "registry.cn-chengdu.aliyuncs.com/allure-commandline"
      imagePullPolicy: "IfNotPresent"
      name: "allure"
      tty: true
      command:
        - "cat"
      env:
        - name: "LANGUAGE"
          value: "en_US:en"
        - name: "LANG"
          value: "en_US.UTF-8"
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "localtime"
          readOnly: false
  restartPolicy: "Never"
  volumes:
    - name: "dockersock"
      hostPath:
        path: "/var/run/docker.sock"
    - name: "localtime"
      hostPath:
        path: "/usr/share/zoneinfo/Asia/Shanghai"
    - name: "m2dir"
      hostPath:
        path: "/opt/m2"
'''
    }
  }

pull project code

stage('pull project code') {
  parallel {
    stage('ui build') {
      when {
        expression {
          env.gitlabBranch == null
        }
      }
      steps {
        sh """
          echo '=================开始拉取项目代码'
        """
 
        git(url: 'git@192.168.117.180:qzcsbj/gift.git', branch: "${BRANCH}", credentialsId: 'qzcsbj_gitlab')
        script {
            COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
            TAG = BUILD_TAG + '-' + COMMIT_ID
            println "Current branch is ${BRANCH}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"
        }           
      }
      post {
        failure {
          dingtalk (
            robot:'dd01',
            type:'MARKDOWN',
            atAll: true,
            title: "notice: 拉取项目代码失败",
            text: ["#### '${JOB_NAME}'项目代码拉取失败\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL})"]
          )
        }
      }
    }
    stage('trigger build') {
      steps {
        sh """
          echo "待更新。。。"
        """
      }
    }
  }
}

mvn package

stage('mvn package') {
  steps {
    container(name: 'maven') {
      sh """
        echo '=================mvn开始打包'
        mvn clean package -Dmaven.test.skip=true
        echo '=================mvn打包完成'
      """
    }
  }
  post {
    failure {
      dingtalk (
        robot:'dd01',
        type:'MARKDOWN',
        atAll: true,
        title: "notice: mvn package失败",
        text: ["#### '${JOB_NAME}'项目mvn package失败\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL})"]
      )
    }
  }
}

build and push image

stage('build and push image') {
  environment {
    HARBOR_USER = credentials('qzcsbj_harbor')
  }
  steps {
    container(name: 'docker') {
      sh """
        echo '=================开始构建镜像'
        docker build -t ${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} .
        docker login -u ${HARBOR_USER_USR} -p ${HARBOR_USER_PSW} ${HARBOR_ADDRESS}
        docker push ${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG}
        echo '=================镜像推送完成'
      """
    }
  }
  post {
    failure {
      dingtalk (
        robot:'dd01',
        type:'MARKDOWN',
        atAll: true,
        title: "notice: 构建或者推送镜像失败",
        text: ["#### '${JOB_NAME}'项目构建或者推送镜像失败\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL})"]
      )
    }
  }
}

deploy to k8s

stage('deploy to k8s') {
  environment {
    MY_KUBECONFIG = credentials('qzcsbj_k8s')
  }
  steps {
    container(name: 'kubectl'){
      sh """
        echo '=================开始部署到k8s'
        /usr/local/bin/kubectl --kubeconfig $MY_KUBECONFIG set image deploy -l app=${IMAGE_NAME} ${IMAGE_NAME}=${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} -n $NAMESPACE
        echo '=================部署到k8s完成'
      """
    }
  }
  post {
    success {
      dingtalk (
        robot:'dd01',
        type:'MARKDOWN',
        atAll: true,
        title: "notice: 自动化部署到k8s完成",
        text: ["#### '${JOB_NAME}'项目自动化部署到k8s完成\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL})"]
      )
    }
    failure {
      dingtalk (
        robot:'dd01',
        type:'MARKDOWN',
        atAll: true,
        title: "notice: 自动化部署到k8s失败",
        text: ["#### '${JOB_NAME}'项目自动化部署到k8s失败\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL})"]
      )
    }
  }
}
 

pull autotest code

stage("pull autotest code"){
  steps{
    sh """
      echo '=================开始拉取自动化测试代码'
    """
    git(
      credentialsId: 'qzcsbj_gitlab',
      url: 'git@192.168.117.180:root/apiautotest.git'
    )
  }
  post {
    failure {
      dingtalk (
        robot:'dd01',
        type:'MARKDOWN',
        atAll: true,
        title: "notice: 拉取自动化测试代码失败",
        text: ["#### 拉取自动化测试代码失败\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL})"]
      )
    }
  }
}

run autotest

stage("run autotest"){
  steps{
    sh """
      mvn clean test -Dsurefire.suiteXmlFiles=testngXML/testng.xml
    """
  }
  post {
    success {
      dingtalk (
        robot:'dd01',
        type:'MARKDOWN',
        atAll: true,
        title: "notice: 自动化测试完成",
        text: ["#### '${JOB_NAME}'项目自动化测试完成\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL}console)"]
      )
    }
    failure {
      dingtalk (
        robot:'dd01',
        type:'MARKDOWN',
        atAll: true,
        title: "notice: 自动化测试运行出错",
        text: ["#### '${JOB_NAME}'项目自动化测试运行出错\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL})"]
      )
    }
  }
}

allure report

stage("allure report"){
  steps{
    sh """
      echo '=================开始生成测试报告'
    """
    allure(
      includeProperties: false,
      jdk: '',
      results: [[path: 'target/allure-results']]
    )
  }
  post {
    success {
      dingtalk (
        robot:'dd01',
        type:'MARKDOWN',
        atAll: true,
        title: "notice: 自动化测试报告已生成,全部通过",
        text: ["#### '${JOB_NAME}'项目自动化测试报告已生成,全部通过\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次测试报告](${BUILD_URL}allure)"]
      )
    }
    unstable {
      dingtalk (
        robot:'dd01',
        type:'MARKDOWN',
        atAll: true,
        title: "notice: 自动化测试报告已生成,有未通过的",
        text: ["#### '${JOB_NAME}'项目自动化测试报告已生成,有未通过的\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次测试报告](${BUILD_URL}allure)"]
      )
    }
    failure {
      dingtalk (
        robot:'dd01',
        type:'MARKDOWN',
        atAll: true,
        title: "notice: 自动化测试报告生成出错",
        text: ["#### '${JOB_NAME}'项目自动化测试报告生成出错\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL})"]
      )
    }
  }
}

08 构建及效果展示

pipeline脚本可以放在流水线项目的脚本框中

图片

也可以放gitlab上,选择SCM

在这里插入图片描述

图片

图片

pipeline脚本放到Jenkinsfile中,Jenkinsfile在gift项目master分支下

在这里插入图片描述

第一次构建,点击“Build Now”,会失败,因为我们使用了参数化构建,这次构建获取不到参数值

在这里插入图片描述

刷新页面,就可以看到参数化构建了,就可以选择要构建的分支

在这里插入图片描述

部署到k8s的通知

在这里插入图片描述

消息

在这里插入图片描述

运行自动化测试的通知

在这里插入图片描述

消息

在这里插入图片描述

生成allure测试报告的通知

在这里插入图片描述

消息

在这里插入图片描述

测试报告的通知可以优化,直接链接到allure测试报告

在这里插入图片描述

测试报告

在这里插入图片描述

图片

声明:封面或正文部分图片来源于网络,如有侵权,请联系删除。

现在我邀请你进入我们的软件测试学习交流群:746506216】,备注“入群”, 大家可以一起探讨交流软件测试,共同学习软件测试技术、面试等软件测试方方面面,还会有免费直播课,收获更多测试技巧,我们一起进阶Python自动化测试/测试开发,走向高薪之路。

喜欢软件测试的小伙伴们,如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一 键三连哦!

在这里插入图片描述

在这里插入图片描述