Operations 7 min read

How to Load Test Phone Number Binding with Dynamic UID‑Based Numbers

This article walks through the challenges of load‑testing a phone‑binding feature that swaps between two number prefixes while preserving the original UID‑derived number, detailing validation rules, a configurable solution, test design, and the full Groovy‑based load‑test script.

FunTester
FunTester
FunTester
How to Load Test Phone Number Binding with Dynamic UID‑Based Numbers

Business Logic

The test scenario requires sending a verification‑code request to obtain a traceNo, then using the user’s login token, the same traceNo, the SMS code, and the phone number to call the bind‑phone API.

Validation Rules

Phone numbers must be 11‑digit strings and belong to allowed prefixes.

No second SMS can be sent within 60 seconds; the SMS validity period matches this interval.

At most 10 SMS can be sent per natural day.

The verification code and traceNo must be retrieved from the send‑code interface.

Solution

All constraints are externalized to configuration files; a service restart applies changes.

Choose the 14 prefix, constructing the test phone as 14+uid.

Fix the verification code in the test environment.

Because uid starts with 9, each user’s phone toggles between 149 and 148; the initial state is 149. The test does not generate a new number each time to avoid accidental collisions and simplify data maintenance.

Test Plan

Combine the send‑code and bind‑phone APIs into a single load‑test scenario, preparing a pool of test users.

Each thread handles one user, repeatedly sending a code and binding a new phone number.

Add a base class field phone and a module‑level field lastTraceNo to pass parameters between steps.

Load‑Test Script

The script uses a custom threadmark to tag each task; the returned traceNo identifies the unique user login request for log tracing.

/**
 * 每个用户先发短信然后绑定手机号,手机号分为148和149切换,后8位于uid后8位.最后会把测试数据固定在149号段
 */
class BindPhone extends OkayBase {
    public static void main(String[] args) {
        def argsUtil = new ArgsUtil(args)
        def thread = argsUtil.getIntOrdefault(0, 100)
        def times = argsUtil.getIntOrdefault(1, 50)
        ClientManage.init(10000, 5000, 0)
        def threads = []
        thread.times {
            def base = getBase(it)
            def info = new UserInfo(base)
            threads << new Bind(info, times)
        }
        new Concurrent(threads, "学生留存1.2.3绑定手机号").start()
        thread.times {
            def base = getBase(it)
            def info = new UserInfo(base)
            info.bindPhoneInit()
        }
        FanLibrary.testOver()
    }
    static class Bind extends ThreadLimitTimesCount<UserInfo> {
        public LoginTel(UserInfo info, int times) {
            super(info, times, null)
        }
        @Override
        protected void doing() throws Exception {
            threadmark = t.bindPhone()
        }
    }
}

Module Methods

/**
 * 获取用户信息
 */
public JSONObject getUserInfo() {
    String url = UserInfoApi.USERINFO;
    JSONObject params = getParams();
    JSONObject response = getPostResponse(url, params);
    output(response);
    return response;
}
/**
 * 绑定手机号
 */
public JSONObject bindPhone(String phone) {
    sendCodeOnline(phone);
    String url = UserInfoApi.BIND_PHONE;
    JSONObject params = getParams();
    params.put("phone", phone);
    params.put("traceno", lastTraceNo);
    params.put("code", Common.TEL_CODE);
    JSONObject response = getPostResponse(url, params);
    output(response);
    return response;
}
/**
 * 绑定手机号,压测用
 */
public String bindPhone() {
    phone = phone.startsWith("149") ? 148 + phone.substring(3) : 149 + phone.substring(3);
    bindPhone(phone);
    return lastTraceNo;
}
/**
 * 初始化手机号,默认id前两位改成14
 */
public JSONObject bindPhoneInit() {
    JSONObject userInfo = getUserInfo();
    String tel = userInfo.getJSONObject("data").getString("tel");
    if (tel.startsWith("149")) return null;
    phone = 149 + phone.substring(3);
    sendCodeOnline(phone);
    String url = UserInfoApi.BIND_PHONE;
    JSONObject params = getParams();
    params.put("phone", phone);
    params.put("traceno", lastTraceNo);
    params.put("code", Common.TEL_CODE);
    JSONObject response = getPostResponse(url, params);
    output(response);
    return response;
}
/**
 * 发送绑定手机号信息
 */
public JSONObject sendCodeOnline(String phone) {
    String url = UserInfoApi.SEND_CODE_ONLINE;
    JSONObject params = getParams();
    params.put("scene_type", 1); // 1绑定手机
    params.put("phone", phone);
    output(phone);
    JSONObject response = getPostResponse(url, params);
    output(response);
    if (isRight(response)) {
        lastTraceNo = response.getJSONObject("data").getString("traceno");
    }
    return response;
}

The article concludes with a disclaimer that the “FunTester” tool is released for community use and should not be reposted without permission.

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.

OperationsAPI automationLoad TestingGroovyphone binding
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.