昨天由于有个项目上线,所以通了个宵,今早起来比较晚,工作进度又落下来一截,心里更加慌慌了。这两天的主要任务还是搬砖了,只有一个地方值得记一下,就是异步执行测试用例,并收集结果。其中涉及到了线程池的创建,基本多线程对象的设计,多用例并行执行。这里面用到的多个用例如果同时使用同一个用户的登录凭证和用户登录凭证缓存。
我的基本方案如下:先用多线程把用例里面的使用到的用户凭证替换成真正的值,然后再去将处理好的用例信息丢给线程池去执行,然后等待所有的线程执行完,测试结果处理等等。
package com.okay.family.common;
import com.okay.family.common.basedata.OkayConstant;
import com.okay.family.common.bean.testcase.CaseRunRecord;
import com.okay.family.common.bean.testcase.request.CaseDataBean;
import com.okay.family.common.enums.RunResult;
import com.okay.family.common.exception.CommonException;
import com.okay.family.common.exception.UserStatusException;
import com.okay.family.utils.RunCaseUtil;
import java.util.concurrent.CountDownLatch;
public class CaseRunThread implements Runnable {
int runId;
CaseDataBean bean;
CaseRunRecord record;
CountDownLatch countDownLatch;
public CaseRunRecord getRecord() {
return record;
}
private CaseRunThread() {
}
public CaseRunThread(CaseDataBean bean, CountDownLatch countDownLatch, int runId) {
this.bean = bean;
this.countDownLatch = countDownLatch;
this.runId = runId;
this.record = new CaseRunRecord();
}
@Override
public void run() {
try {
record = RunCaseUtil.run(bean);
} catch (UserStatusException e) {
record.setResult(RunResult.USER_ERROR.getCode());
} catch (CommonException e) {
record.setResult(RunResult.UNRUN.getCode());
} catch (Exception e) {
record.setResult(RunResult.UNRUN.getCode());
} finally {
countDownLatch.countDown();
}
}
private void init() {
record.setMark(OkayConstant.RUN_MARK.getAndIncrement());
record.setRunId(runId);
record.setCaseId(bean.getId());
//todo:完成基本功能的初始化
}
}
比较简单,具体功能还得后续完善。
package com.okay.family.common;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class OkayThreadPool {
private static ThreadPoolExecutor executor = createPool();
public static void addSyncWork(Runnable runnable) {
executor.execute(runnable);
}
private static ThreadPoolExecutor createPool() {
return new ThreadPoolExecutor(5, 50, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(1000));
}
}
@Override
public CollectionRunSimpleResutl runCollection(RunCollectionBean bean) {
List<CaseDataBean> cases = getCasesDeatil(bean);
CountDownLatch countDownLatch = new CountDownLatch(cases.size());
int andIncrement = OkayConstant.COLLECTION_MARK.getAndIncrement();
List<CaseRunThread> results = new ArrayList<>();
String start = Time.getDate();
cases.forEach(x -> {
CaseRunThread caseRunThread = new CaseRunThread(x, countDownLatch, andIncrement);
OkayThreadPool.addSyncWork(caseRunThread);
results.add(caseRunThread);
});
try {
countDownLatch.await();
} catch (InterruptedException e) {
CommonException.fail("执行用例集失败!");
}
String end = Time.getDate();
//todo:处理结果,记录结果,返回结果
results.forEach(x -> caseService.addRunRecord(x.getRecord()));
Map<Integer, List<Integer>> collect = results.stream().map(x -> x.getRecord().getResult()).collect(Collectors.groupingBy(x -> x));
CollectionRunSimpleResutl res = new CollectionRunSimpleResutl();
//todo:初始化res
return res;
}
@Override
public List<CaseDataBean> getCasesDeatil(RunCollectionBean bean) {
ConcurrentHashMap<Integer, String> certificates = new ConcurrentHashMap<>();
List<CaseDataBean> cases = caseCollectionMapper.getCasesDeatil(bean);
CountDownLatch countDownLatch = new CountDownLatch(cases.size());
cases.forEach(x -> {
new Thread(() -> {
try {
caseService.handleParams(x, certificates);
}finally {
countDownLatch.countDown();
}
}).start();
});
try {
countDownLatch.await();
} catch (InterruptedException e) {
CommonException.fail("初始化用例信息失败!");
}
return cases;
}
下面是caseService.handleParams
方法内容,参数带map
的都是多线程用的
@Override
public void handleParams(CaseDataBean bean) {
JSONObject params = bean.getParams();
JSONObject headers = bean.getHeaders();
handleParams(params);
handleParams(headers);
}
@Override
public void handleParams(CaseDataBean bean, ConcurrentHashMap<Integer, String> map) {
JSONObject params = bean.getParams();
JSONObject headers = bean.getHeaders();
handleParams(params, map);
handleParams(headers, map);
}
/**
* 处理参数中的表达式信息
*
* @param params
*/
public void handleParams(JSONObject params) {
params.keySet().stream().forEach(key ->
{
String value = params.getString(key);
if (value.startsWith(OkayConstant.USER_CERTIFICATE_KEY)) {
int id = SourceCode.changeStringToInt(value.substring(OkayConstant.USER_CERTIFICATE_KEY.length()));
TestUserCheckBean userCheckBean = testUserService.getCertificate(id);
params.put(key, userCheckBean.getCertificate());
} else if (value.startsWith(OkayConstant.RANDOM_KEY)) {
String replace = value.replace(OkayConstant.RANDOM_KEY, Constant.EMPTY);
String[] split = replace.split(",", 2);
params.put(key, SourceCode.getRandomIntRange(SourceCode.changeStringToInt(split[0]), SourceCode.changeStringToInt(split[1])));
}
});
}
/**
* 处理参数中的表达式信息
*
* @param params
*/
public void handleParams(JSONObject params, ConcurrentHashMap map) {
params.keySet().stream().forEach(key ->
{
String value = params.getString(key);
if (value.startsWith(OkayConstant.USER_CERTIFICATE_KEY)) {
int id = SourceCode.changeStringToInt(value.substring(OkayConstant.USER_CERTIFICATE_KEY.length()));
String certificate = testUserService.getCertificate(id, map);
params.put(key, certificate);
} else if (value.startsWith(OkayConstant.RANDOM_KEY)) {
String replace = value.replace(OkayConstant.RANDOM_KEY, Constant.EMPTY);
String[] split = replace.split(",", 2);
params.put(key, SourceCode.getRandomIntRange(SourceCode.changeStringToInt(split[0]), SourceCode.changeStringToInt(split[1])));
}
});
}
Fhaohaizi@163.com
。