Loading

Docker构建Jmeter生成测试报告

环境准备

  • CentOS 7

工具包

Docker构建jmeter运行生成测试报告

准备jmeter包,自己下载也可以通过命令下载

wget --no-check-certificate https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.4.3.tgz

创建并编辑jmeter的Dockerfile

基于jmeter版本5.4.3

[root@localhost jmeter]# vi Dockerfile
FROM java:8

MAINTAINER jmeter_test<binzichen@126.com

ENV MYPATH /jmeterdocker
WORKDIR $MYPATH

ENV JMETER_VERSION=5.4.3
ENV JMETER_HOME=${MYPATH}/apache-jmeter-${JMETER_VERSION}
ENV JMETER_PATH=${JMETER_HOME}/bin:${PATH}
ENV PATH=${JMETER_HOME}/bin:${PATH}

ADD apache-jmeter-${JMETER_VERSION}.tgz ${MYPATH}

构建镜像

[root@localhost jmeter]# docker build -f Dockerfile -t jmeter_test:0.1 .
Sending build context to Docker daemon  202.5MB
Step 1/9 : FROM java:8
8: Pulling from library/java
5040bd298390: Pull complete 
fce5728aad85: Pull complete 
76610ec20bf5: Pull complete 
60170fec2151: Pull complete 
e98f73de8f0d: Pull complete 
11f7af24ed9c: Pull complete 
49e2d6393f32: Pull complete 
bb9cdec9c7f3: Pull complete 
Digest: sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8c0e9d
Status: Downloaded newer image for java:8
 ---> d23bdf5b1b1b
Step 2/9 : MAINTAINER jmeter_test<binzichen@126.com
 ---> Running in 24c7e4f442e1
Removing intermediate container 24c7e4f442e1
 ---> 9fc52580e7ac
Step 3/9 : ENV MYPATH /JmeterDocker
 ---> Running in 107165a88e9f
Removing intermediate container 107165a88e9f
 ---> 323ae31b925f
Step 4/9 : WORKDIR $MYPATH
 ---> Running in f1bf4cea7d33
Removing intermediate container f1bf4cea7d33
 ---> 9b0afa6658f2
Step 5/9 : ENV JMETER_VERSION=5.4.3
 ---> Running in a95aac00f101
Removing intermediate container a95aac00f101
 ---> 0d82ad043dba
Step 6/9 : ENV JMETER_HOME=${MYPATH}/apache-jmeter-${JMETER_VERSION}
 ---> Running in a26c19d0ad10
Removing intermediate container a26c19d0ad10
 ---> 4d910c196696
Step 7/9 : ENV JMETER_PATH=${JMETER_HOME}/bin:${PATH}
 ---> Running in 032804cc7c6f
Removing intermediate container 032804cc7c6f
 ---> 50f383e1b882
Step 8/9 : ENV PATH=${JMETER_HOME}/bin:${PATH}
 ---> Running in 856cf7dddaa1
Removing intermediate container 856cf7dddaa1
 ---> 6c287bdaeef3
Step 9/9 : ADD apache-jmeter-${JMETER_VERSION}.tgz ${MYPATH}
 ---> 6a3b2ca939fd
Successfully built 6a3b2ca939fd
Successfully tagged jmeter_test:0.1

查看镜像

[root@localhost jmeter]# docker images
REPOSITORY    TAG       IMAGE ID       CREATED          SIZE
jmeter_test   0.1       6a3b2ca939fd   13 seconds ago   773MB
java          8         d23bdf5b1b1b   5 years ago      643MB

运行容器

# -v 挂载目录
# -d 后台运行
# --name 容器命名

docker run --name="jmeter_test" \
	    -v /tmp/jmeterspace/test/input/jmx:/jmeterdocker/test/input/jmx \
            -v /tmp/jmeterspace/test/input/testdata:/jmeterdocker/test/input/testdata \
            -v /tmp/jmeterspace/test/report/html:/jmeterdocker/test/report/html \
            -v /tmp/jmeterspace/test/report/jtl:/jmeterdocker/test/report/jtl \
            -v /tmp/jmeterspace/test/report/outputputdata:/jmeterdocker/test/report/outputdata \
            -it -d jmeter_test:0.1

# 查看已启动容器 docker ps
[root@localhost jmeter]# docker ps
CONTAINER ID   IMAGE             COMMAND       CREATED         STATUS         PORTS     NAMES
06878940ac66   jmeter_test:0.1   "/bin/bash"   4 seconds ago   Up 3 seconds             jmeter_test
# 

测试运行生成报告

准备jmx脚本

脚本放在 /tmp/jmeterspace/test/input/jmx 目录下

# 脚本
[root@localhost jmx]# ls
for_docker.jmx

# 运行产生的数据(jtl文件)放在 /tmp/jmeterspace/test/report/jtl 目录下
# 运行产生的报告(HTML文件包)放在 /tmp/jmeterspace/test/report/html 目录下

# 通过docker运行jmeter生成jtl和html包
[root@localhost jmx]# docker exec -it jmeter_test jmeter -n -t /jmeterdocker/test/input/jmx/for_docker.jmx \
                          -l /jmeterdocker/test/report/jtl/html.jtl \
                          -e -o /jmeterdocker/test/report/html/result
Creating summariser <summary>
Created the tree successfully using /jmeterdocker/test/input/jmx/for_docker.jmx
Starting standalone test @ Thu May 19 02:20:36 UTC 2022 (1652926836208)
Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
summary =      4 in 00:00:05 =    0.7/s Avg:  1273 Min:   484 Max:  2813 Err:     2 (50.00%)
Tidying up ...    @ Thu May 19 02:20:42 UTC 2022 (1652926842232)
... end of run

查看报告

# 进入 /tmp/jmeterspace/test/report/html 目录下
[root@localhost /]# cd /tmp/jmeterspace/test/report/html
[root@localhost html]# ls
result
[root@localhost html]# cd result/
[root@localhost result]# ls
content  index.html  sbadmin2-1.0.7  statistics.json
# 可以看到html包已生成

进阶Docker构建Jmeter+Ant生成详细报告

构建jmeter+ant镜像

准备jmeter包和ant包,放在/home/jmeter 目录下

[root@localhost jmeter]# ls
apache-ant-1.10.12-bin.tar.gz  apache-jmeter-5.4.3.tgz

创建并编辑jmeter+ant的Dockerfile

FROM java:8

MAINTAINER jmeter_test<binzichen@126.com

ENV MYPATH /jmeterdocker
WORKDIR $MYPATH

ENV JMETER_VERSION=5.4.3
ENV ANT_VERSION=1.10.12

ENV JMETER_HOME=${MYPATH}/apache-jmeter-${JMETER_VERSION}
ENV JMETER_PATH=${JMETER_HOME}/bin:${PATH}
ENV PATH=${JMETER_HOME}/bin:${PATH}

ENV ANT_HOME=${MYPATH}/apache-ant-${ANT_VERSION}
ENV PATH=${PATH}:${ANT_HOME}/bin

ADD apache-jmeter-${JMETER_VERSION}.tgz ${MYPATH}
ADD apache-ant-${ANT_VERSION}-bin.tar.gz ${MYPATH}

构建镜像 ant_jmeter

[root@localhost jmeter]# docker build -f Dockerfile -t ant_jmeter:0.1 .
Sending build context to Docker daemon  303.5MB
Step 1/13 : FROM java:8
8: Pulling from library/java
5040bd298390: Pull complete 
fce5728aad85: Pull complete 
76610ec20bf5: Pull complete 
60170fec2151: Pull complete 
e98f73de8f0d: Pull complete 
11f7af24ed9c: Pull complete 
49e2d6393f32: Pull complete 
bb9cdec9c7f3: Pull complete 
Digest: sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8c0e9d
Status: Downloaded newer image for java:8
 ---> d23bdf5b1b1b
Step 2/13 : MAINTAINER jmeter_test<binzichen@126.com
 ---> Running in 93942d689465
Removing intermediate container 93942d689465
 ---> 4e5e374bf766
Step 3/13 : ENV MYPATH /jmeterdocker
 ---> Running in b28d2300105e
Removing intermediate container b28d2300105e
 ---> 0b5658636749
Step 4/13 : WORKDIR $MYPATH
 ---> Running in 167b7ef3d9fb
Removing intermediate container 167b7ef3d9fb
 ---> 1eaad545d4a4
Step 5/13 : ENV JMETER_VERSION=5.4.3
 ---> Running in 826a59f5469a
Removing intermediate container 826a59f5469a
 ---> 2ec8f0c390f2
Step 6/13 : ENV ANT_VERSION=1.10.12
 ---> Running in a939b11068f5
Removing intermediate container a939b11068f5
 ---> 1c08f52f2d57
Step 7/13 : ENV JMETER_HOME=${MYPATH}/apache-jmeter-${JMETER_VERSION}
 ---> Running in 7303622586e6
Removing intermediate container 7303622586e6
 ---> e41327572670
Step 8/13 : ENV JMETER_PATH=${JMETER_HOME}/bin:${PATH}
 ---> Running in 84b41fab2213
Removing intermediate container 84b41fab2213
 ---> 6160a2e2c322
Step 9/13 : ENV PATH=${JMETER_HOME}/bin:${PATH}
 ---> Running in 194a8eb21e25
Removing intermediate container 194a8eb21e25
 ---> 090538bf4369
Step 10/13 : ENV ANT_HOME=${MYPATH}/apache-ant-${ANT_VERSION}
 ---> Running in 5baeca3c6212
Removing intermediate container 5baeca3c6212
 ---> c7e14f8ee993
Step 11/13 : ENV PATH=${PATH}:${ANT_HOME}/bin
 ---> Running in bb7c8d5b6433
Removing intermediate container bb7c8d5b6433
 ---> 51a3e6c75aca
Step 12/13 : ADD apache-jmeter-${JMETER_VERSION}.tgz ${MYPATH}
 ---> 6144c52e759a
Step 13/13 : ADD apache-ant-${ANT_VERSION}-bin.tar.gz ${MYPATH}
 ---> fa4362da80f9
Successfully built fa4362da80f9
Successfully tagged ant_jmeter:0.1

查看镜像

[root@localhost jmeter]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
ant_jmeter   0.1       fa4362da80f9   8 seconds ago   815MB
java         8         d23bdf5b1b1b   5 years ago     643MB

运行容器 ant_jmeter

docker run --name="ant_jmeter" \
			-v /tmp/jmeterspace/test/input/jmx:/jmeterdocker/test/input/jmx \
            -v /tmp/jmeterspace/test/input/build:/jmeterdocker/test/input/build \
            -v /tmp/jmeterspace/test/report/html:/jmeterdocker/test/report/html \
            -v /tmp/jmeterspace/test/report/jtl:/jmeterdocker/test/report/jtl \
            -v /tmp/jmeterspace/test/report/outputputdata:/jmeterdocker/test/report/outputdata \
            -it -d ant_jmeter:0.1

# 查看已启动容器 docker ps
[root@localhost jmeter]# docker ps
CONTAINER ID   IMAGE            COMMAND       CREATED          STATUS          PORTS     NAMES
74295fe8b829   ant_jmeter:0.1   "/bin/bash"   16 minutes ago   Up 16 minutes             ant_jmeter

验证jmeter、ant已生效

# jmeter -v 查看jmeter能正常运行
[root@localhost jmeter]# docker exec -it ant_jmeter jmeter -v
    _    ____   _    ____ _   _ _____       _ __  __ _____ _____ _____ ____
   / \  |  _ \ / \  / ___| | | | ____|     | |  \/  | ____|_   _| ____|  _ \
  / _ \ | |_) / _ \| |   | |_| |  _|    _  | | |\/| |  _|   | | |  _| | |_) |
 / ___ \|  __/ ___ \ |___|  _  | |___  | |_| | |  | | |___  | | | |___|  _ <
/_/   \_\_| /_/   \_\____|_| |_|_____|  \___/|_|  |_|_____| |_| |_____|_| \_\ 5.4.3

Copyright (c) 1999-2021 The Apache Software Foundation

# ant -version 查看ant能正常运行
[root@localhost jmeter]# docker exec -it ant_jmeter ant -version
Apache Ant(TM) version 1.10.12 compiled on October 13 2021

镜像内部配置更新

将jmeter extras目录下的ant-jmeter-1.1.1.jar文件拷贝到ant安装目录的lib目录中

[root@localhost jmeter]# docker exec -it ant_jmeter cp /jmeterdocker/apache-jmeter-5.4.3/extras/ant-jmeter-1.1.1.jar /jmeterdocker/apache-ant-1.10.12/lib/

修改jmeter bin目录下jmeter.properties配置

jmeter.save.saveservice.data_type=true
jmeter.save.saveservice.label=true
jmeter.save.saveservice.response_code=true
# response_data is not currently supported for CSV output
jmeter.save.saveservice.response_data=true
# Save ResponseData for failed samples
jmeter.save.saveservice.response_data.on_error=true
jmeter.save.saveservice.response_message=true
jmeter.save.saveservice.successful=true
jmeter.save.saveservice.thread_name=true
jmeter.save.saveservice.time=true
jmeter.save.saveservice.subresults=true
jmeter.save.saveservice.assertions=true
jmeter.save.saveservice.latency=true
# Only available with HttpClient4
jmeter.save.saveservice.connect_time=true
jmeter.save.saveservice.samplerData=true
jmeter.save.saveservice.responseHeaders=true
jmeter.save.saveservice.requestHeaders=true
jmeter.save.saveservice.encoding=true
jmeter.save.saveservice.bytes=true
# Only available with HttpClient4
jmeter.save.saveservice.sent_bytes=true
jmeter.save.saveservice.url=true
jmeter.save.saveservice.filename=true
jmeter.save.saveservice.hostname=true
jmeter.save.saveservice.thread_counts=true
jmeter.save.saveservice.sample_count=true
jmeter.save.saveservice.idle_time=true

jmeter.properties修改

将jmeter.results.shanhe.me.xsl 放入jmeter的extras目录下

# 创建并编辑jmeter.results.shanhe.me.xsl文件 vi jmeter.results.shanhe.me.xsl
[root@localhost jmeter]# vi jmeter.results.shanhe.me.xsl
<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output method="html" indent="no" encoding="UTF-8" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" doctype-system="http://www.w3.org/TR/html4/loose.dtd"/>

    <!-- Defined parameters (overrideable) -->

    <xsl:param    name="titleReport" select="'My Porsche APP API Test Results'"/>

    <xsl:param    name="dateReport" select="'date not defined'"/>

    <xsl:strip-space elements="*"/>

    <xsl:template match="/testResults">

        <html lang="en">

        <head>

            <meta name="Author" content="shanhe.me"/>

            <title><xsl:value-of select="$titleReport" /></title>

            <style type="text/css"><![CDATA[

                * { margin: 0; padding: 0 }

                html, body { width: 100%; height: 100%; background: #b4b4b4; font-size: 12px }

                table { border: none; border-collapse: collapse; table-layout: fixed }

                td { vertical-align: baseline; font-size: 12px }

                #top-panel     { position: absolute; left: 0; top: 0; bottom: 0; display: block }

                #top-panel h1 { margin: 0px 0px 2px; font: 138% verdana,arial,helvetica}

                #top-panel table tr td, table tr th { font-size: 118%; }

                #top-panel table.details tr th{ color: #ffffff; font-weight: bold; text-align:center; background:#2674a6; white-space: nowrap; }

                #top-panel table.details tr td{ background:#eeeee0; white-space: nowrap; }

                #left-panel { position: absolute; left: 0; top: 81px; bottom: 0; width: 300px; overflow: auto; background: #dee4ea }

                #left-panel li.navigation { font-weight: bold; cursor: default; color: #9da8b2; line-height: 18px; background-position: 12px 5px; background-repeat: no-repeat; padding: 0 0 0 25px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAICAYAAAArzdW1AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBQqGbO7BEcAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAKRJREFUGNN1zM0KgkAYheF3RvtXSsGyWhRNaILS7bdt11W0KgJvoPwZp0UlBPUtz3nOJw7Hk7necv5dOA2Qaazo2vZP0LEt9olCVtqQROufKNmuqBuBNAYW4QzXGX6B0bDPcjGnMQYJ8Cg12U59oSzaUJQa4IUAXMclDHwAAn/MxPMw765FZd2QRgopBWmsKCrdfhXnS/4ZYElBXdyxewN008Y8AephLAkqz613AAAAAElFTkSuQmCC) }

                #left-panel li.success { color: #565b60 }

                #left-panel li.failure { color: red }

                #left-panel li { list-style: none; color: black; cursor: pointer }

                #left-panel li.selected { background-repeat: repeat-x; color: white; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAUCAYAAABMDlehAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBQxLTs5O2gAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAEdJREFUCNc1y7ERgEAMA0GNUhIyGqM2uqKgtyWZhE9v53A/7/A6D7BkMDNgy2AroB2wHTCZv5UMOgFLG1bvd7XBckBlwCXjA5wMOF5iOX/MAAAAAElFTkSuQmCC) }

                #left-panel div { line-height: 20px; background-position: 25px 3px; background-repeat: no-repeat; padding: 0 0 0 45px }

                #left-panel div.success { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAOCAYAAADwikbvAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBULEEc6wzcAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAiNJREFUKM99kktIVGEYhp/jzJl08lI6logp2Y2EFkbtaqlFROsWrlq4ioJWQRs37VoUVItWkYEVRGSBlhleCpywDEWxTEuxcURTZ6YzxzP/5WshCOHUt36f93kXnyMi5Lsnb4clI4s4fhkXzp5w8mWcfHBvfEpUxVdCUUU6lUPNHuD86cYtBQX5GhPrM7hRg7GaSDRg2vuUd90WuOPVsOyqy6FFo2yOQHlU1S9z9dZT+S/8I7GCLlkAN4eyAf56mnT6Fy1HLnGuuYa++MS/4e74qMRqfXLaJ9BpfnsrLC0m2BYuoqwUbj/+274JD43OEqmexwvW8NUKXnaZtVSS1pNtAAyOvyC6v48HnUNb4Z7PH8UtTlIQWA5tb2RhYY7kz3l2FleytJYg/qWb8t2KZ/0PN+1hgI6uEUr2jpHKpGlquExVaS0VbjUZL7WxaqIXK6ADQ0n9GNfv9XCttWnD/O57t0TKFklnF3g5fJ/seoaa2D4O1x0F4PlgO9oIftbgFgYMfLgjACGqj0vlsddoUnj+Kt/mxunq72RP+UGqYjWMTA7R+b6dUCSEGEF5hoJQip6BaFs4HJtCyRrKs6wHCovDip/kys0WWpovMpOYBCtoT2N9B5uzWG0Zid8gnFrVFEQDtBaUrxEgXBimaEeER2/uIiK4roPOaMRYjBKsFly3fOO3G06dETGCWIsYjckprMphtEKMAQtgsMYi1mJMQHJ6xvkDKQoyphCzkl0AAAAASUVORK5CYII=) }

                #left-panel div.failure { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAOCAYAAADwikbvAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBUJOEC5CU8AAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAeVJREFUKM+NkDtok2EUhp8vl9ZLo/EyKI6KFgqCKC4OClrBWUQEcRRx1cGpk3WyInWrgoMZKkW8thYaEYQ0i7WC2ngrNDTERHJvkv/L/3//dxwc7F8jeOAsh/c973OOEhG61aPnaen7maXYt4MLZ4+pbppQt+F06jNH3QWOb8pxUs+SmJzjv83hxY8SVy3wNdtVneiHqe54IhLoB4/TUkyMyOrKj5yXoVtPZK02kLyYK7OnlqFWzgcCGtUC/YUJ3n5a/jd28tU7ORTN0myUA6Jms8bpWIa798elqzn1fokjThrpVBC3ETzNbYAuca59j/Hp+b/N869Tsk8tgVMCXQk+RlfQuk1/tMLMwzsSMCcm5zjhvoR2AdpF0GuwO4aqttS05ZSbZHhsBoAIwI83Cdkd/460XDAOG02d24MxvlR8dsUUh3f2UHaEtgdbWCHz4oZwcVCp66PP5FLhKjEc8DXaCMsNy8DYn/SnZ+L0hhWOb/F8yLs9fDtwk8j+VpqwrlC34PrgGEu2bhlYhZ1b8dncq3AMeBaUr/k6NUyk4ChKzu+N2hc6Bqody+WDG8g2fLatD7F3axjPgmvAtYJvIbouhhIRrl0ZktnkBGIt1gqeMXQ8D2MMiCIUCqFEsFhEQMSykCuqX0MzLAUJTzRsAAAAAElFTkSuQmCC) }

                #left-panel div.detail { display: none }

                #right-panel { position: absolute; right: 0; top: 81px; bottom: 0; left: 301px; overflow: auto; background: white }

                #right-panel .group { font-size: 12px; font-weight: bold; line-height: 16px; padding: 0 0 0 18px; counter-reset: assertion; background-repeat: repeat-x; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAQCAYAAADXnxW3AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBUkDq8pxjkAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAADdJREFUCNdVxrERwDAMAzGK0v47eS6Z927SpMFBAAbkvSvnRk5+7K5cVfLMyN39bWakJAjA5xw9R94jN3tVhVEAAAAASUVORK5CYII=) }

                #right-panel .zebra { background-repeat: repeat; padding: 0 0 0 18px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAmCAYAAAAFvPEHAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBYWFlNztEcAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAABdJREFUCNdjYKAtePv5338mBgYGBpoQAGy1BAJlb/y6AAAAAElFTkSuQmCC) }

                #right-panel .data { line-height: 19px; white-space: nowrap }

                #right-panel pre.data { white-space: pre }

                #right-panel tbody.failure { color: red }

                #right-panel td.key { min-width: 108px }

                #right-panel td.delimiter { min-width: 18px }

                #right-panel td.assertion:before { counter-increment: assertion; content: counter(assertion) ". " }

                #right-panel td.assertion { color: black }

                #right-panel .trail { border-top: 1px solid #b4b4b4 }

            ]]></style>

            <script type="text/javascript"><![CDATA[

                var onclick_li = (function() {

                    var last_selected = null;

                    return function(li) {

                        if( last_selected == li )

                            return;

                        if( last_selected )

                            last_selected.className = "";

                        last_selected = li;

                        last_selected.className = "selected";

                        document.getElementById("right-panel").innerHTML = last_selected.firstChild.nextSibling.innerHTML;

                        return false;

                    };

                })();

                var patch_timestamp = function() {

                    var spans = document.getElementsByTagName("span");

                    var len = spans.length;

                    for( var i = 0; i < len; ++i ) {

                        var span = spans[i];

                        if( "patch_timestamp" == span.className )

                            span.innerHTML = new Date( parseInt( span.innerHTML ) );

                    }

                };

                var patch_navigation_class = (function() {

                    var set_class = function(el, flag) {

                        if(el) {

                            el.className += flag ? " success" : " failure";

                        }

                    };

                    var traverse = function(el, group_el, flag) {

                        while(1) {

                            if(el) {

                                if(el.className == 'navigation') {

                                    set_class(group_el, flag);

                                    group_el = el;

                                    flag = true;

                                } else {

                                    var o = el.firstChild;

                                    o = o ? o.className : null;

                                    flag = flag ? (o == 'success') : false;

                                }

                                el = el.nextSibling;

                            } else {

                                set_class(group_el, flag);

                                break;

                            }

                        }

                    };

                    return function() {

                        var o = document.getElementById("result-list");

                        o = o ? o.firstChild : null;

                        if(o)

                            traverse(o, null, true);

                    };

                })();

                window.onload = function() {

                    patch_timestamp();

                    patch_navigation_class();

                    var o = document.getElementById("result-list");

                    o = o ? o.firstChild : null;

                    o = o ? o.nextSibling : null;

                    if(o)

                        onclick_li(o);

                };

            ]]></script>

        </head>

        <body>

            <div id="top-panel">

                <xsl:call-template name="pageHeader" />

                <xsl:call-template name="summary" />

                <hr size="1" width="100%" align="center" />

            </div>

            <div id="left-panel">

                <ol id="result-list">

                    <xsl:for-each select="*">

                        <!-- group with the previous sibling -->

                        <xsl:if test="position() = 1 or @tn != preceding-sibling::*[1]/@tn">

                            <li class="navigation">Thread: <xsl:value-of select="@tn"/></li>

                        </xsl:if>

                        <li onclick="return onclick_li(this);">

                            <div>

                                <xsl:attribute name="class">

                                    <xsl:choose>

                                        <xsl:when test="@s = 'true'">success</xsl:when>

                                        <xsl:otherwise>failure</xsl:otherwise>

                                    </xsl:choose>

                                </xsl:attribute>

                                <xsl:value-of select="@lb"/>

                            </div><div class="detail">

                                <div class="group">Sampler</div>

                                <div class="zebra">

                                    <table>

                                        <tr><td class="data key">Thread Name</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@tn"/></td></tr>

                                        <tr><td class="data key">Timestamp</td><td class="data delimiter">:</td><td class="data"><span class="patch_timestamp"><xsl:value-of select="@ts"/></span></td></tr>

                                        <tr><td class="data key">Time</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@t"/> ms</td></tr>

                                        <tr><td class="data key">Latency</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@lt"/> ms</td></tr>

                                        <tr><td class="data key">Bytes</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@by"/></td></tr>

                                        <tr><td class="data key">Sample Count</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@sc"/></td></tr>

                                        <tr><td class="data key">Error Count</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@ec"/></td></tr>

                                        <tr><td class="data key">Response Code</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@rc"/></td></tr>

                                        <tr><td class="data key">Response Message</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@rm"/></td></tr>

                                    </table>

                                </div>

                                <div class="trail"></div>

                                <xsl:if test="count(assertionResult) &gt; 0">

                                    <div class="group">Assertion</div>

                                    <div class="zebra">

                                        <table>

                                            <xsl:for-each select="assertionResult">

                                                <tbody>

                                                    <xsl:attribute name="class">

                                                        <xsl:choose>

                                                            <xsl:when test="failure = 'true'">failure</xsl:when>

                                                            <xsl:when test="error = 'true'">failure</xsl:when>

                                                        </xsl:choose>

                                                    </xsl:attribute>

                                                    <tr><td class="data assertion" colspan="3"><xsl:value-of select="name"/></td></tr>

                                                    <tr><td class="data key">Failure</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="failure"/></td></tr>

                                                    <tr><td class="data key">Error</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="error"/></td></tr>

                                                    <tr><td class="data key">Failure Message</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="failureMessage"/></td></tr>

                                                </tbody>

                                            </xsl:for-each>

                                        </table>

                                    </div>

                                    <div class="trail"></div>

                                </xsl:if>

                                <div class="group">Request</div>

                                <div class="zebra">

                                    <table>

                                        <tr><td class="data key">Method/Url</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="method"/><xsl:text> </xsl:text><xsl:value-of select="java.net.URL"/></pre></td></tr>

                                        <tr><td class="data key">Query String</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="queryString"/></pre></td></tr>

                                        <tr><td class="data key">Cookies</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="cookies"/></pre></td></tr>

                                        <tr><td class="data key">Request Headers</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="requestHeader"/></pre></td></tr>

                                    </table>

                                </div>

                                <div class="trail"></div>

                                <div class="group">Response</div>

                                <div class="zebra">

                                    <table>

                                        <tr><td class="data key">Response Headers</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="responseHeader"/></pre></td></tr>

                                        <tr><td class="data key">Response Data</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="responseData"/></pre></td></tr>

                                        <tr><td class="data key">Response File</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="responseFile"/></pre></td></tr>

                                    </table>

                                </div>

                                <div class="trail"></div>

                            </div>

                        </li>

                    </xsl:for-each>

                </ol>

            </div>

            <div id="right-panel"></div>

        </body>

        </html>

    </xsl:template>

    <xsl:template name="pageHeader">

        <h1><xsl:value-of select="$titleReport" /></h1>

        <table width="100%">

            <tr>

                <td align="left">Date report: <xsl:value-of select="$dateReport" /></td>

                <td align="right">Designed for use with <a href="http://jmeter.apache.org/">JMeter</a> and <a href="http://ant.apache.org">Ant</a>.</td>

            </tr>

        </table>

        <hr size="1" />

    </xsl:template>

    <xsl:template name="summary">

        <table align="center" class="details" border="0" cellpadding="5" cellspacing="2" width="100%">

            <tr valign="top">

                <th>用例总数</th>

                <th>失败数</th>

                <th>通过率</th>

                <th>平均消耗时间</th>

                <th>最小响应时间</th>

                <th>最大响应时间</th>

            </tr>

            <tr valign="top">

                <xsl:variable name="allCount" select="count(/testResults/*)" />

                <xsl:variable name="allFailureCount" select="count(/testResults/*[attribute::s='false'])" />

                <xsl:variable name="allSuccessCount" select="count(/testResults/*[attribute::s='true'])" />

                <xsl:variable name="allSuccessPercent" select="$allSuccessCount div $allCount" />

                <xsl:variable name="allTotalTime" select="sum(/testResults/*/@t)" />

                <xsl:variable name="allAverageTime" select="$allTotalTime div $allCount" />

                <xsl:variable name="allMinTime">

                    <xsl:call-template name="min">

                        <xsl:with-param name="nodes" select="/testResults/*/@t" />

                    </xsl:call-template>

                </xsl:variable>

                <xsl:variable name="allMaxTime">

                    <xsl:call-template name="max">

                        <xsl:with-param name="nodes" select="/testResults/*/@t" />

                    </xsl:call-template>

                </xsl:variable>

                <xsl:attribute name="class">

                    <xsl:choose>

                        <xsl:when test="$allFailureCount &gt; 0">Failure</xsl:when>

                    </xsl:choose>

                </xsl:attribute>

                <td align="center">

                    <xsl:value-of select="$allCount" />

                </td>

                <td align="center">

                    <xsl:value-of select="$allFailureCount" />

                </td>

                <td align="center">

                    <xsl:call-template name="display-percent">

                        <xsl:with-param name="value" select="$allSuccessPercent" />

                    </xsl:call-template>

                </td>

                <td align="center">

                    <xsl:call-template name="display-time">

                        <xsl:with-param name="value" select="$allAverageTime" />

                    </xsl:call-template>

                </td>

                <td align="center">

                    <xsl:call-template name="display-time">

                        <xsl:with-param name="value" select="$allMinTime" />

                    </xsl:call-template>

                </td>

                <td align="center">

                    <xsl:call-template name="display-time">

                        <xsl:with-param name="value" select="$allMaxTime" />

                    </xsl:call-template>

                </td>

            </tr>

        </table>

    </xsl:template>

    <xsl:template name="min">

        <xsl:param name="nodes" select="/.." />

        <xsl:choose>

            <xsl:when test="not($nodes)">NaN</xsl:when>

            <xsl:otherwise>

                <xsl:for-each select="$nodes">

                    <xsl:sort data-type="number" />

                    <xsl:if test="position() = 1">

                        <xsl:value-of select="number(.)" />

                    </xsl:if>

                </xsl:for-each>

            </xsl:otherwise>

        </xsl:choose>

    </xsl:template>

    <xsl:template name="max">

        <xsl:param name="nodes" select="/.." />

        <xsl:choose>

            <xsl:when test="not($nodes)">NaN</xsl:when>

            <xsl:otherwise>

                <xsl:for-each select="$nodes">

                    <xsl:sort data-type="number" order="descending" />

                    <xsl:if test="position() = 1">

                        <xsl:value-of select="number(.)" />

                    </xsl:if>

                </xsl:for-each>

            </xsl:otherwise>

        </xsl:choose>

    </xsl:template>

    <xsl:template name="display-percent">

        <xsl:param name="value" />

        <xsl:value-of select="format-number($value,'0.00%')" />

    </xsl:template>

    <xsl:template name="display-time">

        <xsl:param name="value" />

        <xsl:value-of select="format-number($value,'0 ms')" />

    </xsl:template>

</xsl:stylesheet>

# 把jmeter.results.shanhe.me.xsl文件从linux复制到jmeter的extras目录下
[root@localhost jmeter]# docker cp jmeter.results.shanhe.me.xsl ant_jmeter:/jmeterdocker/apache-jmeter-5.4.3/extras

# 通过容器查看 jmeter.results.shanhe.me.xsl 是否复制过去
[root@localhost jmeter]# docker exec -it ant_jmeter ls /jmeterdocker/apache-jmeter-5.4.3/extras
GrafanaJMeterTemplate.json  execcode.bsh                         proxycert.sh
Test.jmx                    expand.png                           remote.bsh
addons.txt                  jmeter-results-detail-report_21.xsl  schematic.cmd
addons.xml                  jmeter-results-report_21.xsl         schematic.sh
ant-jmeter-1.1.1.jar        jmeter.results.shanhe.me.xsl         schematic.xml
build.xml                   printvars.bsh                        schematic.xsl
collapse.png                proxycert.cmd                        startup.bsh

# 可以看到jmeter.results.shanhe.me.xsl 已存在

创建并编辑build.xml文件放入/jmeterdocker/test/input/build 目录下

# 进入/jmeterdocker/test/input/build目录
cd /jmeterdocker/test/input/build
# vim build.xml
<?xml version="1.0" encoding="UTF-8"?>

<project name="ant-jmeter-test" default="run" basedir=".">
    <tstamp>
        <format property="time" pattern="yyyyMMddhhmm" />
    </tstamp>
    <!-- 需要改成自己本地的 Jmeter 目录-->  
    <property name="jmeter.home" value="/jmeterdocker/apache-jmeter-5.4.3" />
    <!-- jmeter生成jtl格式的结果报告的路径--> 
    <property name="jmeter.result.jtl.dir" value="/jmeterdocker/test/report/jtl" />
    <!-- jmeter生成html格式的结果报告的路径-->
    <property name="jmeter.result.html.dir" value="/jmeterdocker/test/report/html" />
	<!-- jmeter运行jmx脚本的路径-->
    <property name="jmeter.run.jmx.dir" value="/jmeterdocker/test/input/jmx" />
    <!-- jmeter生成html格式的结果报告的前缀-->  
    <property name="ReportName" value="report" />
    <property name="jmeter.result.jtlName" value="${jmeter.result.jtl.dir}/html.jtl" />
    <property name="jmeter.result.htmlName" value="${jmeter.result.html.dir}/${ReportName}.html" />
    
    <target name="run">
        <antcall target="test" />
        <antcall target="report" />
    </target>
    
    <target name="test">
        <taskdef name="jmeter" classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask" />
        <jmeter jmeterhome="${jmeter.home}" resultlog="${jmeter.result.jtlName}">
             <!-- 声明要运行的脚本。"*.jmx"指包含此目录下的所有jmeter脚本-->
            <testplans dir="${jmeter.run.jmx.dir}" includes="*.jmx" />
            <property name="jmeter.save.saveservice.output_format" value="xml"/>
        </jmeter>
    </target>
    
    <path id="xslt.classpath">
        <fileset dir="${jmeter.home}/lib" includes="xalan*.jar"/>
        <fileset dir="${jmeter.home}/lib" includes="serializer*.jar"/>
    </path> 
    
    <target name="report">
        <tstamp><format property="report.datestamp" pattern="yyyy/MM/dd HH:mm"/></tstamp>
        <xslt 
              classpathref="xslt.classpath"
              force="true"
              in="${jmeter.result.jtlName}"
              out="${jmeter.result.htmlName}"
              style="${jmeter.home}/extras/jmeter.results.shanhe.me.xsl" >
              <param name="dateReport" expression="${report.datestamp}"/>
        </xslt>
                <!-- 因为上面生成报告的时候,不会将相关的图片也一起拷贝至目标目录,所以,需要手动拷贝 --> 
        <copy todir="${jmeter.result.html.dir}">
            <fileset dir="${jmeter.home}/extras">
                <include name="collapse.png" />
                <include name="expand.png" />
            </fileset>
        </copy>
    </target>
</project>

测试docker构建jmeter+ant运行jmx脚本生成html报告

[root@localhost jmeter]# docker exec -it ant_jmeter ant -buildfile /jmeterdocker/test/input/build/build.xml
Buildfile: /jmeterdocker/test/input/build/build.xml

run:

test:
   [jmeter] Executing test plan: /jmeterdocker/test/input/jmx/for_docker.jmx ==> /jmeterdocker/test/report/jtl/html.jtl
   [jmeter] Creating summariser <summary>
   [jmeter] Created the tree successfully using /jmeterdocker/test/input/jmx/for_docker.jmx
   [jmeter] Starting standalone test @ Thu May 19 11:50:19 UTC 2022 (1652961019519)
   [jmeter] Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
   [jmeter] summary =      4 in 00:00:03 =    1.4/s Avg:   643 Min:   281 Max:  1600 Err:     2 (50.00%)
   [jmeter] Tidying up ...    @ Thu May 19 11:50:22 UTC 2022 (1652961022836)
   [jmeter] ... end of run

report:
     [xslt] Processing /jmeterdocker/test/report/jtl/html.jtl to /jmeterdocker/test/report/html/report.html
     [xslt] Loading stylesheet /jmeterdocker/apache-jmeter-5.4.3/extras/jmeter.results.shanhe.me.xsl

BUILD SUCCESSFUL
Total time: 5 seconds

查看生成的html报告

# 进入 /tmp/jmeterspace/test/report/html/目录
[root@localhost jmeter]# cd /tmp/jmeterspace/test/report/html/
[root@localhost html]# ls
collapse.png  expand.png  report.html

提交新镜像docker commit

由于最初构建镜像部分环境配置、文件缺失不够完善,为了保持当前容器的状态,就可以通过commit来提交,获得一个镜像。

[root@localhost report]# docker ps
CONTAINER ID   IMAGE            COMMAND       CREATED             STATUS             PORTS     NAMES
74295fe8b829   ant_jmeter:0.1   "/bin/bash"   About an hour ago   Up About an hour             ant_jmeter

# commit成一个新镜像
[root@localhost report]# docker commit -a="Ant_Jmeter" -m="add lib/exc/*.jar and new properies new build.xml" 74295fe8b829 ant_jmeter:1.0
sha256:14dd543cae3c3afae38d0e8d8603fdea88b74e7a6d4a10a737cff7fd8fdaf77e
[root@localhost report]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED             SIZE
ant_jmeter   1.0       14dd543cae3c   4 seconds ago       984MB
ant_jmeter   0.1       fa4362da80f9   About an hour ago   815MB
java         8         d23bdf5b1b1b   5 years ago         643MB
posted @ 2022-05-19 10:50  Binzichen  阅读(771)  评论(0)    收藏  举报