The book follows the scenario of distributed performance testing framework use case scheme (I). Scheme 2 carries out more complex test cases in the form of JAR package class methods. In the next phase, technical verification will be conducted based on Docker.

Pressure test scenario based on jar package class method

The performance test script is based on the FunTester performance test framework. Today we share a use case that has been written in advance and exposes some parameters. The test scenarios for this type of performance test case have been set in advance, and the parameterization is currently only differentiated in the number of test threads, soft start time, termination conditions, etc.

The addition of such use cases requires redeployment of services, so it is suitable for stable use cases, such as benchmark performance test cases, which can be used for performance inspection, periodic performance regression, and other testing scenarios. This use-case form is different from the single-request and multi-request patterns in use-case scenario (I), and scripting already supports more use-case scenarios, including single-link, multi-link, and full-link tests. Interfaces that need to be signed on every request are also good choices.

PS: completely abandoned the local shell command execution way, too complex, uncontrollable things more.

To realize the Demo

Here I wrote a test class, achieve no reference method, the base type parameters method, the String object type, String array type parameter performance of the four methods of reflection Demo function based on com. Funtester. Frame. The execute. ExecuteSource class, The main function of this class is to execute the methods in the JAR package. I’ll leave the code for this class at the end, but you can also read the original article to see the latest code in the repository.

Here is the code for the test class:

public class Test3 extends SourceCode {

    private static final Logger logger = LogManager.getLogger(Test3.class);

    public static void main(String[] args) {
        ExecuteSource.executeMethod("com.funtest.javatest.Test3.funtest");
        ExecuteSource.executeMethod("com.funtest.javatest.Test3.funtest"."java.lang.Integer"."1");
        ExecuteSource.executeMethod("com.funtest.javatest.Test3.funtester"."java.lang.Integer"."1");
        ExecuteSource.executeMethod("com.funtest.javatest.Test3.funtester"."java.lang.String"."33,3234,43,24,23,4,22");

    }


    public static void funtest(a) {
        output(Time.getDate());
    }


    public static void funtest(Integer i) {
        output(i + TAB + Time.getDate());
    }

    public static void funtester(int i) {
        output(i * 10 + TAB + Time.getDate());
    }

    public static void funtester(String[] i) {
        output(i.length * 10 + TAB + Time.getDate());
    }

    public static void funtester(String i) { funtester(i.split(COMMA)); }}Copy the code

Console output:

INFO->The current user: oker, working directory: / Users/oker/IdeaProjects funtester/system coding formats: utf-8, Mac OS X system version: 10.16
INFO->The 2021-05-13 18:24:05
INFO->1 the 2021-05-13 18:24:05
WARN->Method property handling error!
INFO->10 the 2021-05-13 18:24:05
INFO->70 the 2021-05-13 18:24:05

Process finished with exit code 0

Copy the code

Reflection executes Groovy class method NoSuchMethodException to answer this error.

When Java reflection calls a method, it doesn’t recognize an overloaded method that takes an underlying type (such as int). Sometimes it finds it, sometimes it doesn’t, and I suspect it’s random. However, changing the java.lang.Integer parameter type is perfectly fine, which should be taken into account when writing use cases in the future.

There is also a catch, Java reflection execution method parameter String[], I have tried several ways, but now give up. Because my use cases are written in the Main method of the Groovy class. So in the future you’ll need to add a method for all use cases in the JAR package:


    / * * *@param args
     */
    public static void main(String args) {
        main(args.split(COMMA));
    }

Copy the code

Use case to create

A single interface

For simple use cases, the standard use case format is as follows:

public static void main(String[] args) {
        ClientManage.init(60.60.0."".0);
        ArgsUtil util = new ArgsUtil(args);
        int thread = util.getIntOrdefault(0.2);
        int times = util.getIntOrdefault(1.10);
        Constant.RUNUP_TIME = util.getIntOrdefault(2.10);
        MySqlTest.LOG_KEY = false;
        String url = "http://localhost:12345/test/qps";
        JSONObject params = new JSONObject();
        params.put("name"."FunTester");
        params.put("password"."123456798");
        Sign.sign(params);
        HttpGet httpGet = getHttpGet(url,params);
        HttpGet httpGet = getHttpGet(url);
        RequestThreadTimes requestThreadTimes = new RequestThreadTimes(httpGet, times, null);
        Concurrent funTester = new Concurrent(requestThreadTimes, thread, "FunTester tests reflection execution");
        funTester.start();
    }
Copy the code

Still USES the com. Funtester. Frame. Thread. RequestThreadTimes multi-threaded task object class as a demo, and Java, but this class has been my mark is out of date, I wrote a multi-threaded object, more suitable for now the company’s project, And compatible with the com. Funtester. Frame. Thread. RequestThreadTimes and com. Funtester. Base. The constaint. ThreadLimitTimeCount function of a class.

Multi-interface and link testing

These two differences are not large, need to implement the test method separately, you can see the link design and implementation ideas in the single link performance test practice article. Just share the script here, as follows:


import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.funtester.base.bean.AbstractBean
import com.funtester.base.constaint.ThreadLimitTimesCount
import com.funtester.frame.execute.Concurrent
import com.funtester.httpclient.ClientManage
import com.funtester.utils.ArgsUtil
import com.okayqa.composer.base.OkayBase
import com.okayqa.composer.function.Mirro
import com.okayqa.composer.function.OKClass

class Login_collect_uncollect extends OkayBase {

    public static void main(String[] args) {
        ClientManage.init(10.5.0."".0)
        def util = new ArgsUtil(args)
        def thread = util.getIntOrdefault(0.30)
        def times = util.getIntOrdefault(1.40)

        def tasks = []

        thread.times {
            tasks << new FunTester(it, times)
        }

        new Concurrent(tasks, "Resource Library 1.4 Login > Query > Favorites > Unfavorites Link Pressure Test").start()

        allOver()
    }

    private static class FunTester extends ThreadLimitTimesCount<Integer> {

        OkayBase base

        def mirro

        def clazz

        FunTester(Integer integer, int times) {
            super(integer, times, null)}@Override
        void before(a) {
            super.before()
            base = getBase(t)
            mirro = new Mirro(base)
            clazz = new OKClass(base)
        }

        @Override
        protected void doing(a) throws Exception {

            def klist = mirro.getKList()
            def karray = klist.getJSONArray("data")
            K ks
            karray.each {
                JSONObject parse = JSON.parse(JSON.toJSONString(it))
                if (ks == null) {
                    def level = parse.getIntValue("node_level")
                    def type = parse.getIntValue("ktype")
                    def id = parse.getIntValue("id")
                    ks = new K(id, type, level)
                }
            }
            JSONObject response = clazz.recommend(ks.id, ks.type, ks.level)
            def minis = []
            int i = 0
            response.getJSONArray("data").each {
                if (i++ < 2) {
                    JSONObject parse = JSON.parse(JSON.toJSONString(it))
                    int value = parse.getIntValue("minicourse_id")
                    minis << value
                }
            }
            clazz.unCollect(random(minis))

            mirro.getMiniCourseListV3(ks.id, ks.type, 0, ks.level)
        }
    }

    private static class K extends AbstractBean {

        int id

        int type

        int level

        K(int id, int type, int level) {
            this.id = id
            this.type = type
            this.level = level
        }
    }

}


Copy the code

Use case transmission

Upload the use case

There is nothing to be said for this, just write a useful example, package and compile, and then restart the service.

More on the legendary Java hot update and ClassLoader loading external classes and JAR files and other high-end technologies, you can choose to explore.

Allocate cases

Adopt the same way as in the use case scheme idea (1). In all the ideas, the idea of allocating use cases is the same, but the difference is the design and implementation of task class objects.

Case execution

This is the way to execute a specific use case in a test task, as described in the Demo beginning with this article. The code for the execution class is as follows:

package com.funtester.frame.execute;

import com.alibaba.fastjson.JSON;
import com.funtester.base.exception.FailException;
import com.funtester.frame.SourceCode;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

public class ExecuteSource extends SourceCode {

    private static Logger logger = LogManager.getLogger(ExecuteSource.class);

       /** ** execute method * <p> to prevent compilation error, use list </p> **@param params
     */
    public static Object executeMethod(List<String> params) {
        Object[] objects = params.subList(1, params.size()).toArray();
        return executeMethod(params.get(0), objects);
    }

    /** ** execute method * <p> to prevent compilation error, use list </p> **@param params
     */
    public static Object executeMethod(String[] params) {
        return executeMethod(Arrays.asList(params));
    }

    /** * Executes a specific method, providing an internal method call * <p> overloaded method </p> ** Error if the argument is an underlying data type@param path
     * @param paramsTpey
     */
    public static Object executeMethod(String path, Object... paramsTpey) {
        int length = paramsTpey.length;
        if (length % 2= =1) FailException.fail("Wrong number of arguments. Should be even.");
        String className = path.substring(0, path.lastIndexOf("."));
        String methodname = path.substring(className.length() + 1); Class<? > c =null;
        Object object = null;
        try {
            c = Class.forName(className);
            object = c.newInstance();
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
            logger.warn("Error creating instance object :{}", className, e);
        }
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            if(! method.getName().equalsIgnoreCase(methodname))continue;
            try {
                Class[] classs = new Class[length / 2];
                for (int i = 0; i < paramsTpey.length; i +=2) {
                    classs[i / 2] = Class.forName(paramsTpey[i].toString());// Arguments to the underlying data type here will cause an error, but will not affect the following calls
                }
                method = c.getMethod(method.getName(), classs);
            } catch (NoSuchMethodException | ClassNotFoundException e) {
                logger.warn("Method property handling error!");
            }
            try {
                Object[] ps = new Object[length / 2];
                for (int i = 1; i < paramsTpey.length; i +=2) {
                    String name = paramsTpey[i - 1].toString();
                    String param = paramsTpey[i].toString();
                    Object p = param;
                    if (name.contains("Integer")) {
                        p = Integer.parseInt(param);
                    } else if (name.contains("JSON")) {
                        p = JSON.parseObject(param);
                    }
                    ps[i / 2] = p;
                }
                method.invoke(object, ps);
            } catch (IllegalAccessException | InvocationTargetException e) {
                logger.warn("Reflection execution method failed :{}", path, e);
            }
            break;
        }
        return null; }}Copy the code

I have deleted some temporarily useless methods. If you are interested, you can go to the warehouse to have a look.


FunTester.Tencent Cloud Author of the Year,Boss direct hire contract author.Official GDevOps media partner, non-famous test development, welcome to follow.

  • FunTester test framework architecture diagram
  • Soft start of performance test
  • Multi-item login kick test case
  • In manometry, the delay of asynchronous write interfaces is measured
  • Use Case Scheme of Distributed Performance Testing Framework (PART 1)
  • Automated testing for Agile teams
  • A complete guide to automated Testing Frameworks
  • Moco framework interface hit ratio statistics practice
  • Unscripted test
  • The Definitive Guide to Java Performance