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.
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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
