在最近的工作中,挫败感极大,我做了深刻反思,得出来一个道理:如是观照,实事求是

原因比较复杂,其中一项是因为某一批接口测试需求比较紧,我之前一直的思路就是尽可能模拟真实数据,使用多用户进行性能测试,一般测试前都需要尽量大量的数据准备工作。但是这次不灵了,接口之间的参数依赖过于复杂,如果真写起来,可真就是把端上的工作重新做一遍,不值当的。

所以我取了个巧,在模拟用户造数据的时候,我直接复制了浏览器里面的接口请求,然后通过不断的刷这个接口去造数据,就不用去一个参数一个参数的写请求了。顺便还能给端上同学写一个简单的性能测试工具Demo,方便他们做一些简单的性能测试。

首先我去解析GETPOST请求,然后通过工具类FunRequest生成一个HttpRequestBase,然后验证一下请求,就可以正常进行性能测试阶段了。

复制请求

这里我采用了复制curl的格式的方式,因为其他的方式数据量太大了,比较复杂,解析起来困难,容易出BUG

复制浏览器请求

GET 请求

分享一下复制的结果,删除了域名。

curl 'https://j****.cn/home/course_list?_=1611648498164&custom_directory_id=4630377&origin=0&page=1&page_size=10' \
  -H 'Connection: keep-alive' \
  -H 'sec-ch-ua: "Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"' \
  -H 'DNT: 1' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36' \
  -H 'requestid: 010427916771' \
  -H 'Accept: application/json, text/javascript, */*; q=0.01' \
  -H 'X-Requested-With: XMLHttpRequest' \
  -H 'is_new_okay: 1' \
  -H 'Sec-Fetch-Site: same-origin' \
  -H 'Sec-Fetch-Mode: cors' \
  -H 'Sec-Fetch-Dest: empty' \
  -H 'Referer: https://jiaoshi-dev.xk12.cn/' \
  -H 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8' \
  -H 'Cookie: db_log=1; org_id=640; user_action_cookie=user_action_87cf9c4d-1e22-4c8c-982e-a4419fa6dc1b_62951571858; teacher_id=9fec845f498a47abb68426c14f90693e' \
  --compressed

POST 请求

分享一下复制的结果,删除了域名,too!

curl 'https://j****.cn/myResourcePool/deleteResource' \
  -H 'Connection: keep-alive' \
  -H 'sec-ch-ua: "Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"' \
  -H 'DNT: 1' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36' \
  -H 'requestid: 011342477158' \
  -H 'Accept: application/json, text/javascript, */*; q=0.01' \
  -H 'X-Requested-With: XMLHttpRequest' \
  -H 'is_new_okay: 1' \
  -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \
  -H 'Origin: https://jiaoshi-dev.xk12.cn' \
  -H 'Sec-Fetch-Site: same-origin' \
  -H 'Sec-Fetch-Mode: cors' \
  -H 'Sec-Fetch-Dest: empty' \
  -H 'Referer: https://jiaoshi-dev.xk12.cn/myResourcePool_vm/my_resources' \
  -H 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8' \
  -H 'Cookie: db_log=1; org_id=640; user_action_cookie=user_action_87cf9c4d-1e22-4c8c-982e-a4419fa6dc1b_62951571858; teacher_id=9fec845f498a47abb68426c14f90693e' \
  --data-raw 'res_id=2317045&res_type=3' \
  --compressed

生成 HttpRequestBase 对象

这里我是把复制的请求写到本地的一个文本文件中,首先读取,然后遍历处理。

public static HttpRequestBase getRequest(String path) {
    def fileinfo = WriteRead.readTxtFileByLine(LONG_Path + path).stream().map {it.trim()}
    def base = new CurlRequestBase()
    fileinfo.each {
        if (it.startsWith("curl")) {
            def split = it.split(" ", 2)
            def type = split[0]
            def value = split[1]
            base.url = value.substring(value.indexOf('h'), value.lastIndexOf("'"))
        } else if (it.startsWith("-H")) {
            def split = it.split(" ", 2)[1].split(": ")
            base.headers << getHeader(split[0].substring(1), split[1].substring(0, split[1].lastIndexOf("'")))
        } else if (it.startsWith("--data-raw")) {
            base.params = getJson(it.substring(it.indexOf("'") + 1, it.lastIndexOf("'")).split("&"))
            base.type = RequestType.POST
        }
    }
    base.type == RequestType.GET ? FunRequest.isGet().setUri(base.url).addHeader(base.headers).getRequest() : FunRequest.isPost().setUri(base.url).addHeader(base.headers).addParams(base.params).getRequest()
}

中间用到了一个CurlRequestBase内部静态类。

static class CurlRequestBase {

        String url

        RequestType type =RequestType.GET

        List<Header> headers = new ArrayList<>()

        JSONObject params = new JSONObject()


    }

性能测试

这个比较容易,接入之前的性能测试框架即可。

public static void main(String[] args) {
        def request = getRequest("get")
        output FanLibrary.getHttpResponse(request)

        def thread = new RequestThreadTimes<HttpRequestBase>(request, 100)
        new Concurrent(thread,30,"FunTester get请求测试").start()

        testOver()
    }

控制台输出

响应的数据量有点大,这里就不放响应结果了,直接放性能测试结果。

INFO-> gc回收线程开始了
INFO-> 线程:FunTester get请求测试16,执行次数100错误次数: 0,总耗时12.92 s
······省略········
INFO-> 线程:FunTester get请求测试19,执行次数100错误次数: 0,总耗时14.017 s
INFO-> 总计30个线程共用时14.033 s,执行总数:3000,错误数:0,失败数:0
INFO-> 数据保存成功文件名/Users/fv/Documents/workspace/fun/long/data/FunTester get请求测试2021012617384730
INFO-> 
~~~~~~~~~~~~~~~~~~~~ JSON ~~~~~~~~~~~~~~~~~~~~
  {
   . "rt":132,
   . "total":3000,
   . "qps":225.86109542631283,
   . "failRate":0.0,
   . "threads":30,
   . "startTime":"2021-01-26 17:38:47",
   . "endTime":"2021-01-26 17:39:01",
   . "errorRate":0.0,
   . "executeTotal":3000,
   . "mark":"FunTester get请求测试20210126173847",
   . "table":"\r\n\t\t\t\tFunTester get请求测试30·····省略,见下图····\r\n"
  }
~~~~~~~~~~~~~~~~~~~~ JSON ~~~~~~~~~~~~~~~~~~~~
INFO-> 
                FunTester get请求测试30

    >>响应时间分布图,横轴排序分成桶的序号,纵轴每个桶的中位数<<
        --<中位数数据最小值为:93 ms,最大值:204 ms>--
                                                                  ██ 
                                                                  ██ 
                                                                  ██ 
                                                               ▅▅ ██ 
                                                            ▃▃ ██ ██ 
                                                         ▃▃ ██ ██ ██ 
                                                   ▂▂ ▇▇ ██ ██ ██ ██ 
                                          ▁▁ ▄▄ ▇▇ ██ ██ ██ ██ ██ ██ 
                                 ▂▂ ▅▅ ▇▇ ██ ██ ██ ██ ██ ██ ██ ██ ██ 
                     ▁▁ ▄▄ ▅▅ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 
            ▃▃ ▅▅ ▇▇ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 
   ▂▂ ▅▅ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 
▃▃ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 

INFO-> gc回收线程结束了

Process finished with exit code 0

最后的响应结果需要使用等宽字体查看才行,如果系统默认的字是非等宽的,请参照下图:

FunTester性能测试结果

关于如何使用性能测试框架和生成性能测试结果,有兴趣的可以翻一翻以前的文章。


FunTester,非著名测试开发,文章记录学习和感悟,欢迎关注,交流成长。


↙↙↙阅读原文可查看相关链接,并与作者交流