Operations 13 min read

Script‑Based Performance Testing Scenarios Using the FunTester Framework

This article introduces a flexible, script‑driven performance testing approach built on the FunTester framework, explains how to write Groovy/Java test scripts, demonstrates a demo implementation with reflection‑based execution, and covers test case creation, transmission, and execution for HTTP‑based load testing.

FunTester
FunTester
FunTester
Script‑Based Performance Testing Scenarios Using the FunTester Framework

The author revisits two previous performance‑testing solutions and presents a third, script‑based approach that runs test cases directly as Groovy or Java scripts on a performance‑testing service, eliminating the need to package test cases into JAR files.

Using the FunTester framework, scripts can be uploaded to the server (or stored in a database or Git) and executed without complex file management. This method supports HTTP protocol testing for single‑link, multi‑link, and full‑link scenarios, and can be extended to other protocols such as Dubbo, Socket, Redis, and MySQL.

Demo Implementation

A sample test class named Share demonstrates reflection‑based execution of four method types (no‑arg, primitive parameter, String object, and String array). The class relies on com.funtester.frame.execute.ExecuteSource to invoke methods inside a JAR package.

import com.funtester.config.Constant
import com.funtester.frame.execute.Concurrent
import com.funtester.frame.thread.RequestThreadTimes
import com.funtester.httpclient.ClientManage
import com.funtester.httpclient.FunLibrary
import com.funtester.utils.ArgsUtil
import org.apache.http.client.methods.HttpGet

class Share extends FunLibrary {
    public static void main(String[] args) {
        ClientManage.init(10, 5, 0, EMPTY, 0);
        def util = new ArgsUtil(args)
        int thread = util.getIntOrdefault(0, 20);
        int times = util.getIntOrdefault(1, 100);
        String url = "http://localhost:12345/m";
        HttpGet get = getHttpGet(url);
        Constant.RUNUP_TIME = 0;
        RequestThreadTimes task = new RequestThreadTimes(get, times);
        new Concurrent(task, thread, "本地固定QPS测试").start();
        testOver();
    }
}

The execution method reads the script file and runs it via ExecuteGroovy.executeScript :

String s = RWUtil.readTxtByString("/Users/oker/IdeaProjects/funtester/src/test/groovy/com/funtest/groovytest/Share.groovy");
ExecuteGroovy.executeScript(s);

To enable parameterization, a wrapper method test(String params) splits a comma‑separated string and forwards the arguments to main :

public static void test(String params) {
    main(params.split(COMMA))
}

Running the parameterized method looks like:

ExecuteGroovy.executeFileMethod("/Users/oker/IdeaProjects/funtester/src/test/groovy/com/funtest/groovytest/Share.groovy", "test", "20,100");

Test Case Creation

A Groovy script named Login_collect_uncollect shows a single‑link test case that uses various utility classes to perform login, query, collect, and un‑collect actions. The script defines an inner class K to hold ID, type, and level information.

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, "资源库1.4登录>查询>收藏>取消收藏链路压测").start()
        allOver()
    }
    private static class FunTester extends ThreadLimitTimesCount
{
        // implementation omitted for brevity
    }
    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 }
    }
}

Test Case Transmission

Test cases are treated as simple String objects; they can be uploaded, saved, or edited by storing the script text in a database or Git repository.

Test Execution

The ExecuteSource class provides reflective utilities to invoke arbitrary methods based on a string path and parameter list. It handles parameter type conversion (e.g., Integer, JSON) and logs any reflection errors.

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);
    public static Object executeMethod(List
params) {
        Object[] objects = params.subList(1, params.size()).toArray();
        return executeMethod(params.get(0), objects);
    }
    public static Object executeMethod(String[] params) {
        return executeMethod(Arrays.asList(params));
    }
    public static Object executeMethod(String path, Object... paramsTpey) {
        // reflection logic omitted for brevity
        return null;
    }
}

The article concludes with links to the FunTester repository and other related technical articles.

JavaPerformance Testingload testingDistributed TestingGroovyFunTester
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.