多线程执行用例能很大的节约时间,而JUnit4(org.junit.experimental.ParallelComputer)本身是支持多线程的(以后会专门写一篇介绍JUnit的多线程执行),但是experimental实验性质的,本文多线程执行用例是自己构造线程池,一个线程去执行一个用例,当然这要求你的用例间不能有交集。
只运行上一次失败的用例,JUnit4(org.junit.experimental.max.MaxCore)本身是支持失败用例优先,但是也会执行上次成功的用例; 比如有用例1,2,3;这次失败了2,下次执行2,1,3;我们想要的只执行2。
本文是以JUnit4---实践二:拓展之运行指定类的某些测试方法-自动生成该文件, JUnit4---实践一:运行指定类的某些测试方法的基础上做了修改,可以先阅读下,再看本文。
项目(实现了自定义注解@Concurrent,多线程执行用例)是通过ant执行JUnit和JUnit-report,生成报告,并生成错误日志;根据扫日志,找出失败用例。
package com.weibo.failmethods;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import junit.framework.Test;
import org.junit.runner.JUnitCore;
import org.junit.runner.Request;
import org.junit.runner.Result;
/**
* @author hugang 根据日志 找出Failure 和 Error
*/
public class FindFailTest {
public static void main(String[] args) throws IOException, InterruptedException {
// 日志路径
String PATH = System.getProperty("user.dir")
+ "/src/com/weibo/failmethods/TEST-com.weibo.cases.suite.WangleiTestSuite.txt";
// 文本每一行
List strList = new ArrayList();
// 行号
List flags = new ArrayList();
// 记录FAILED和ERROR
Set failSet = new TreeSet();
// String regexStr =
// "(Testcase:\\s\\w*([\\w]*.{3,}\\w*.):\\sFAILED)|(Testcase:\\s\\w*([\\w]*.{3,}\\w*.):\\sCaused\\san\\sERROR)";
Pattern p = Pattern.compile("Testcase");
Matcher m;
int i = 0;
try {
Reader re = new FileReader(new File(PATH));
BufferedReader bre = new BufferedReader(re);
while (bre.ready()) {
String str = bre.readLine();
strList.add(str);
m = p.matcher(str);
// 匹配后,记录匹配的行号
if (m.find()) {
flags.add(i);
System.out.println("find " + i);
}
i++;
}
for (int k = 0; k < flags.size(); k++) {
// 去除SKIPPED, 只存 FAILED和ERROR
if (!strList.get(flags.get(k)).contains("SKIPPED")) {
// 从文本中取满足匹配的那行字符串
failSet.add(strList.get(flags.get(k)));
}
}
bre.close();
re.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Map> myClassMethodMap = new LinkedHashMap();
List className = new ArrayList();
List methodName = new ArrayList();
for (Iterator it = failSet.iterator(); it.hasNext();) {
// System.out.println(it.next().toString());
// Testcase:
// testAPIRequest(com.weibo.cases.xuelian.FeedWithDarwinTagsForMovieStatusTest):
// FAILED
// 取出类和方法
String str = it.next().toString();
int classBegin = str.indexOf("(");
int classEnd = str.indexOf(")");
// 类名
String classPart = str.substring(classBegin + 1, classEnd).concat(
".class");
// 方法名, 加上双引号,方便写文件时,注解中方法的添加
String methodPart = "\"" + str.substring(10, classBegin) + "\"";
// 聚合 class-method 一对多
if (myClassMethodMap.containsKey(classPart)) {
// 拿到之前的class 对应的list, 并在该list下新增 method
List methodList = myClassMethodMap.get(classPart);
methodList.add(methodPart);
myClassMethodMap.put(classPart, methodList);
} else {
// 第一次添加该class时
List firstMethod = new ArrayList();
firstMethod.add(methodPart);
myClassMethodMap.put(classPart, firstMethod);
}
System.out.println(classPart + " " + methodPart);
}
System.out.println(className.size());
System.out.println(methodName.size());
System.out.println(myClassMethodMap);
// 写执行错误的文件 ,myClassMethodMap要跑的数据, PATH为元文件(实时更新为错误日志)
new WriteFailTest().writeJavaClass(myClassMethodMap, PATH);
}
}
//auto java file generated on: 2015.02.05 15:37:11,898
package com.weibo.failmethods;
import java.text.SimpleDateFormat;
import java.io.*;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.junit.runner.*;
import com.weibo.cases.hugang.*;
import com.weibo.cases.xiaoyu.*;
import com.weibo.cases.wanglei16.*;
import com.weibo.cases.xuelian.*;
import com.weibo.cases.lingna.*;
import com.weibo.cases.maincase.*;
@SuiteClassesMethods(className = {
com.weibo.cases.xiaoyu.StatusesLatestTimelineStatusTest.class},
methodsNumPerClass = {
1},
methodsName = {
"testLatestTimelineConsolePrivate"})
public class AutoFailMethodsTest {
final static int THREADCOUNT = 50;
public static void main(String[] args) throws Exception {
String path = "/Users/hugang/workspace/testMblog/src/com/weibo/failmethods/TEST-com.weibo.cases.suite.WangleiTestSuite.txt";
Class clazz = AutoFailMethodsTest.class;
SuiteClassesMethods scm = clazz.getAnnotation(SuiteClassesMethods.class);
Class>[] className = scm.className();
int[] methodsNumPerClass = scm.methodsNumPerClass();
String[] methodsName = scm.methodsName();
Map, List> myClassMethodMap = new LinkedHashMap();
List> listMethodsName = new ArrayList>();
int k = 0;
for (int i = 0; i < className.length; i++) {
List temp = new ArrayList();
for (int m = 0; m < methodsNumPerClass[i]; m++) {
temp.add(methodsName[k]);
k++;
}
listMethodsName.add(i, temp);
myClassMethodMap.put(className[i], listMethodsName.get(i));
}
List methodsResult = new ArrayList();
int failNum = 0;
int successNum = 0;
long runTime = 0L;
ExecutorService executorService = Executors.newFixedThreadPool(THREADCOUNT);
List> listFr = new ArrayList>();
long startTime = System.currentTimeMillis();
for(Map.Entry, List> entry : myClassMethodMap.entrySet()){
Class testClass = entry.getKey();
List failMethod = entry.getValue();
for(int i = 0; i < failMethod.size(); i++){
Future fr = executorService.submit(new ThreadRunTest(testClass, failMethod.get(i)));
listFr.add(fr);
}
}
for(Future fr : listFr){
try{
while(!fr.isDone());
Result result = fr.get();
if(result.wasSuccessful()){
successNum++;
}else{
failNum++;
methodsResult.add(result);
}
}catch (InterruptedException e) {
e.printStackTrace();
}finally{
executorService.shutdown();
}
}
long endTime = System.currentTimeMillis();
runTime = endTime - startTime;
try{
String filePath = System.getProperty("user.dir") + "/src/com/weibo/failmethods/Result.txt";
File file = new File(filePath);
if(!file.exists()){
file.createNewFile();
}
FileOutputStream fop = new FileOutputStream(file);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss,SSS");
fop.write("## Second Time's Result generated on: ".getBytes());
fop.write(sdf.format(new Date()).getBytes());
fop.write("\n".getBytes());
StringBuffer sb = new StringBuffer();
sb.append("===================== 结果集 =====================");
sb.append("\n");
sb.append("用例总数:" + methodsName.length);
sb.append(", 成功数:" + successNum);
sb.append(", 失败数:" + failNum);
sb.append(", 运行时间:" + (runTime/1000)/60 + " 分钟 " + (runTime/1000)%60 + " 秒");
sb.append("\n");
sb.append("=================================================");
sb.append("\n");
sb.append("\n");
fop.write(sb.toString().getBytes());
for(int j = 0; j < methodsResult.size(); j++){
byte[] fail = methodsResult.get(j).getFailures().toString().getBytes();
fop.write(fail);
fop.write("\n".getBytes());
fop.write("\n".getBytes());
}
fop.flush();
fop.close();
new WriteLogTest().writeFailLog(filePath, path);
}catch(IOException e){
e.printStackTrace();
}
}
}
由于是通过WriteFailTest类生成的文件,并没添加注释,最重要的部分就是将每个用例提交给一个线程去执行,获得每个线程执行的结果fr.get()。统计结果集:
通过WriteLogTest.java将本次失败用例的信息回写到元日志TEST-com.weibo.cases.suite.WangleiTestSuite.txt,为下次执行用。
重复2,执行FindFailTest.java和AutoFailMethodsTest.java;生成只执行第一步没过的用例的结果:
依次类推...