一.需求(小组内接口自动化由 junit4 cases+ 方法重试 [@Retry]+HttpClient[调接口,获取数据,jackson 反序列化为 java 对象]+ 线程池 [并发执行用例]+ 数据库连接池 [获取测试账号,账号存在 mysql 中] 组成,由 ant 生成 junit 报告)
最近,小组内有人提出了,是否能单独运行某些失败的 cases,因为在 eclipse 中只能运行一个类的全部 cases 或某个 cases; 不能指定跑单个类某些方法或多个类的某些方法。juint4 中有@RunWith(Suite.class) 和 @Suite.SuiteClasses({A.class, B.class}) 聚合类 A 和 B;但是不能指定某些方法。
在 Ant 执行 Junit 中有个 formatter(结果的类型 type:xml, plain, brief or failure),其中 type=failure, collects all failing testXXX() methods and creates a new TestCase which delegates only these failing methods. 我尝试了,执行 ant ***.xml 报错为 substring 空指针,查看源码 FailureRecorder.java; 该文件就是用来创建新的测试类(聚合失败 cases),但是用 Junit3(继承 TestCase),并有 bug,不能用:
public static class TestInfos implements Comparable {
private final String className;
private final String methodName;
public TestInfos(Test test) {
this.className = test.getClass().getName();
String _methodName = test.toString();
this.methodName = _methodName.substring(0, _methodName.indexOf(40));
}
...
}
TestInfos 带参构造函数 TestInfos(Test test) 中
_methodName.substring(0, _methodName.indexOf(40)) // ( 的 ASCII 码:40
_methodName 并没有 ‘(’ 导致空指针。
二.方案
使用注解
@SuiteClassesMethods;指定
Class<?>[] className 类名
int[] methodsNumPerClass(); 每个类对应的方法数量
String[] methodsName() 方法名
实现代码如下:
SuiteClassesMethods.java
package com.weibo.failmethods;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({java.lang.annotation.ElementType.TYPE})
public @interface SuiteClassesMethods {
public Class<?>[] className();
public int[] methodsNumPerClass();
public String[] methodsName();
}
FailMethodsTest.java
package com.weibo.failmethods;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.junit.runner.JUnitCore;
import org.junit.runner.Request;
import org.junit.runner.Result;
import com.weibo.cases.hugang.AccountUpdatePrivacyCommentTest;
import com.weibo.cases.hugang.AttitudeRemindTest;
/**
* @author hugang
*/
@SuiteClassesMethods(className = { AccountUpdatePrivacyCommentTest.class,
AttitudeRemindTest.class }, methodsNumPerClass = { 2, 1 }, methodsName = {
"testAllNonAttentionByComment", "testFriendAllAttByComment",
"testAttitudeSomeOneQunStatusPushCount2" })
public class FailMethodsTest {
public static void main(String[] args) throws Exception {
Class<FailMethodsTest> clazz = FailMethodsTest.class;
SuiteClassesMethods scm = clazz.getAnnotation(SuiteClassesMethods.class);
Class<?>[] className = scm.className();
int[] methodsNumPerClass = scm.methodsNumPerClass();
String[] methodsName = scm.methodsName();
// 根据注解 传的方法,与每个测试类对应
Map<Class<?>, List<String>> myClassMethodMap = new LinkedHashMap();
List<List<String>> listMethodsName = new ArrayList<List<String>>();
// k游标,遍历methodsName
int k = 0;
// 外层for循环,表示类个数
for (int i = 0; i < className.length; i++) {
List<String> temp = new ArrayList<String>();
// 内层for循环,为每个类赋方法
for (int m = 0; m < methodsNumPerClass[i]; m++) {
temp.add(methodsName[k]);
k++;
}
listMethodsName.add(i, temp);
myClassMethodMap.put(className[i], listMethodsName.get(i));
}
JUnitCore junitRunner = new JUnitCore();
List<Result> methodsResult = new ArrayList<Result>();
// 运行测试方法
for(Map.Entry<Class<?>, List<String>> entry : myClassMethodMap.entrySet()){
Class testClass = entry.getKey();
List<String> failMethod = entry.getValue();
for(int i = 0; i < failMethod.size(); i++){
Request request = Request.method(testClass, failMethod.get(i));
Result result = junitRunner.run(request);
methodsResult.add(result);
}
}
// 将错误集记录到file中
try{
File file = new File("/Users/hugang/Desktop/failMethod.txt");
FileOutputStream fop = new FileOutputStream(file);
for(int j = 0; j < methodsResult.size(); j++){
byte[] fail = methodsResult.get(j).getFailures().toString().getBytes();
fop.write(fail);
}
fop.flush();
fop.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
结果: