Operations 12 min read

How to Diversify Request Parameters for Robust Performance Testing

This article explains practical techniques for generating varied request parameters—such as random numbers, thread‑safe identifiers, random strings, and upstream data extraction—to avoid identical inputs in multi‑call performance tests and improve test reliability.

FunTester
FunTester
FunTester
How to Diversify Request Parameters for Robust Performance Testing

Background

When testing a single‑link performance scenario, the same API may be called multiple times with identical parameters, which limits the test's ability to uncover issues because only one of the responses is actually used for subsequent steps.

Business‑Unrelated Parameters

Random Numbers

A simple utility method generates a random integer between 1 and num (inclusive):

public static int getRandomInt(int num) {
    return random.get().nextInt(num) + 1;
}

public static int getRandomIntRange(int start, int end) {
    if (end <= start) return TEST_ERROR_CODE;
    return random.get().nextInt(end - start) + start;
}

Thread‑Safe Random Values

To obtain unique values per thread, a static AtomicInteger is used as a base, combined with a per‑thread counter:

public class ParamMark extends SourceCode implements MarkThread, Cloneable, Serializable {
    private static final long serialVersionUID = -5532592151245141262L;
    public static AtomicInteger threadName = new AtomicInteger(getRandomIntRange(1000, 9000));
    String name;
    int num = getRandomIntRange(100, 999) * 1000;
    @Override
    public String mark(ThreadBase threadBase) {
        return name + num++;
    }
    @Override
    public ParamMark clone() {
        return new ParamMark();
    }
    public ParamMark() {
        this.name = threadName.getAndIncrement() + EMPTY;
    }
    public ParamMark(String name) {
        this();
        this.name = name;
    }
}

The combination of threadName and num guarantees globally unique identifiers without relying on timestamps.

Random Strings

Utility methods generate fixed‑length random strings, either with or without digits:

static String getString(int length) {
    StringBuffer sb = new StringBuffer();
    if (length < 1) return sb.toString();
    for (int i = 1; i <= length; i++) {
        sb.append(getChar());
    }
    return sb.toString();
}

static String getStringWithoutNum(int length) {
    StringBuffer sb = new StringBuffer();
    if (length < 1) return sb.toString();
    for (int i = 1; i <= length; i++) {
        sb.append(getWord());
    }
    return sb.toString();
}

public static long getNanoMark() {
    return System.nanoTime();
}

When stricter uniqueness is required, nanosecond timestamps can be used.

Business‑Related Parameters

Random Values Within a Range

For parameters that must fall within a specific numeric range (e.g., 0‑7), random selection is applied:

public static <F extends Number> F random(F... fs) {
    return fs[getRandomInt(fs.length) - 1];
}

public static String random(String... fs) {
    if (ArrayUtils.isEmpty(fs)) ParamException.fail("Array cannot be empty!");
    return fs[getRandomInt(fs.length) - 1];
}

public static <F extends Object> F random(List<F> list) {
    if (list == null || list.isEmpty()) ParamException.fail("Array cannot be empty!");
    return list.get(getRandomInt(list.size()) - 1);
}

Groovy scripts can use the range syntax 0..10 for similar effect.

Upstream Interface Data

Objects required for the test can be fetched from an upstream API, parsed, and stored in a List<K> for later random selection:

def klist = mirro.getKList();
List<K> kss = new ArrayList<>();
for (Object item : klist.getJSONArray("data")) {
    JSONObject parse = JSON.parse(JSON.toJSONString(item));
    int level = parse.getIntValue("node_level");
    int type = parse.getIntValue("ktype");
    int id = parse.getIntValue("id");
    kss.add(new K(id, type, level));
}

K ks = kss.get(0);
K ks2 = kss.get(1);
K ks3 = kss.get(3);
clazz.recommend(ks3.id, ks3.type, ks3.level);
clazz.recommend(ks2.id, ks2.type, ks2.level);
JSONObject response = clazz.recommend(ks.id, ks.type, ks.level);

Pre‑Creating Data for Multi‑Threaded Tests

A LinkedBlockingQueue<String> holds prepared SQL statements or other payloads, ensuring each thread retrieves a unique item:

static LinkedBlockingQueue<String> sqls = new LinkedBlockingQueue<>();

public static boolean addWork(String sql) {
    try {
        sqls.put(sql);
    } catch (InterruptedException e) {
        logger.warn("Failed to add DB task!", e);
        return false;
    }
    return true;
}

static String getWork() {
    String sql = null;
    try {
        sql = sqls.poll(SqlConstant.MYSQLWORK_TIMEOUT, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
        logger.warn("Failed to get DB task!", e);
    } finally {
        return sql;
    }
}

Estimating the required volume beforehand prevents test failures due to insufficient data; alternatively, a dedicated thread can continuously feed the queue.

Conclusion

By incorporating random numbers, thread‑safe identifiers, random strings, upstream data extraction, and pre‑created data pools, testers can significantly increase the diversity of request parameters in performance testing, reducing false positives and better simulating real‑world usage patterns.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaautomationPerformance Testingthread safetyparameter diversificationrandom data generation
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

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.