In the recent work, frustration is great, I did a profound reflection, come out a truth: so look, seek truth from facts.

The reasons are complicated, one of which is that a certain batch of interface tests have relatively tight requirements. My previous thinking is to simulate real data as much as possible and conduct performance tests with multiple users. Generally, a large amount of data preparation is needed before tests. But this time it did not work, the parameter dependency between interfaces is too complex, if really written, it is really to do the work on the end again, not worth it.

So I took the trick of copying the interface request in the browser directly when simulating user creation, and then constantly brushing the interface to create data, instead of having to write requests one parameter at a time. By the way, I can also write a simple performance test tool Demo for the students on the end, which is convenient for them to do some simple performance tests.

First I parse the GET and POST requests, then generate an HttpRequestBase from the utility class FunRequest, verify the request, and proceed to the normal performance testing phase.

Copy the request

Here, I adopted the format of copying curl, because other methods have too much data and are complicated, difficult to parse and prone to bugs.

A GET request

Share the result of the replication, deleting the domain name.

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-requisition-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' \ --compressedCopy the code

A POST request

Share the result of the replication, delete the domain name, 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-requisition-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' \ --compressedCopy the code

Generates an HttpRequestBase object

Here I write the copied request to a local text file, read it first, and then iterate over it.

    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()
    }

Copy the code

A CurlRequestBase internal static class is used in the middle.

static class CurlRequestBase {

        String url

        RequestType type =RequestType.GET

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

        JSONObject params = new JSONObject()


    }
Copy the code

The performance test

This is easy to do by plugging into the previous performance testing framework.

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 request test").start()

        testOver()
    }
Copy the code

Console output

The amount of data in the response is a little bit large, so instead of showing the response results, I’m going to show the performance test results.

INFO-> GC collection thread started! INFO-> Thread :FunTester get request test16, times of execution:100, number of errors:0, total time:12.92INFO-> Thread :FunTester Get request test19, times of execution:100, number of errors:0, total time:14.017S the INFO - > total30Number of threads, shared time:14.033S, total number of executions:3000, error number:0, number of failures:0INFO-> Data saved successfully! Name of the file: / Users/fv/Documents/workspace/fun /long/data/FunTester Get request test2021012617384730INFO - > ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ JSON ~ ☢ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ~ > {> (1)."rt":132> 1)."total":3000> 1)."qps":225.86109542631283> 1)."failRate":0.0> 1)."threads":30> 1)."startTime":"The 2021-01-26 17:38:47"> 1)."endTime":"The 2021-01-26 17:39:01"> 1)."errorRate":0.0> 1)."executeTotal":3000> 1)."mark":"FunTester get request test 20210126173847"> 1)."table":"\ r \ n \ t \ t \ \ t tFunTester get request testing 30... ellipsis, see below... \ r \ n">} ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ~ JSON ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ ~ ☢ ☢ ~ ~ ~ the INFO - > FunTester get request test30>> Response time distribution diagram, the ordinal number of buckets sorted on the horizontal axis, median of each bucket on the vertical axis << --< the minimum value of median data is:93Ms, maximum value:204Ms > - █ █ █ █ █ █ ▅ ▅ █ █ ▃ ▃ █ █ █ █ ▃ ▃ █ █ █ █ █ █ ▂ ▂ ▇ ▇ █ █ █ █ █ █ █ █ ▁ ▁ ▄ ▄ ▇ ▇ █ █ █ █ █ █ █ █ █ █ █ █ ▂ ▂ ▅ ▅ ▇ ▇ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ ▁ ▁ ▄ ▄ ▅ ▅ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ ▃ ▃ ▅ ▅ ▇ ▇ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ ▂ ▂ ▅ ▅ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ ▃ ▃ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ INFO - > gc thread is over! Process finished with exit code0
Copy the code

The final response result can only be viewed using a monospaced font. If the default word is non-monospaced, please refer to the following figure:

For information on how to use the performance testing framework and generate performance test results, check out the previous articles.


FunTester, non-famous test development, the article records learning and comprehension, welcome to pay attention to, exchange growth.
  • GiteeaddressGitee.com/fanapi/test…
  • GitHubaddressGithub.com/JunManYuanL…

  • 2020 FunTester self-summary
  • Best practices for avoiding PPT automation
  • Fixed QPS pressure test
  • How do I test a probabilistic business interface
  • JSON objects mark syntax validation classes
  • There is no data to drive automated tests
  • Understand stress and load testing in test models
  • Simplify test cases
  • Agile testing is two or three things
  • Automated test lifecycle
  • Moco fixed QPS interface upgrade compensation mechanism
  • Pressure test results were corrected using microreference tests