一.需求(小组内接口自动化由junit4+重试[用例]+HttpClient[调接口,获取数据]+多线程[执行用例]+数据库连接池[测试账号]组成,由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(); } } }
结果:
三.优化输出
由于之前输出结果不太明晰,修改FailMethodsTest.java
package com.weibo.failmethods; /** * @author hugang */ //import java.io.File; //import java.io.FileOutputStream; //import java.io.IOException; import java.io.*; import java.text.SimpleDateFormat; import java.util.*; //import org.junit.runner.JUnitCore; //import org.junit.runner.Request; //import org.junit.runner.Result; import org.junit.runner.*; import com.weibo.cases.hugang.*; @SuiteClassesMethods(className = { AttitudeRemindTest.class }, methodsNumPerClass = { 1 }, methodsName = { "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>(); // 失败数 int failNum = 0; // 成功数 int successNum = 0; // 运行时间 long runTime = 0L; // 运行测试方法 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); runTime += result.getRunTime(); // 只添加失败的 if(result.wasSuccessful()){ successNum++; }else{ failNum++; methodsResult.add(result); } } } // for(int j = 0; j < methodsResult.size(); j++){ // methodsResult.get(j).wasSuccessful(); // methodsResult.get(j).getFailures().toString(); // } // 将错误集记录到file中 try{ String filePath = System.getProperty("user.dir") + "/src/com/weibo/failmethods/failMethods.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 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(); }catch(IOException e){ e.printStackTrace(); } } }输出结果:有结果统计
参考:
junit wiki: http://junit.sourceforge.net/javadoc/overview-summary.html