JUnit4---实践二:拓展之运行指定类的某些测试方法-自动生成该文件

一.背景

之前介绍过:

JUnit4---实践一:运行指定类的某些测试方法,当重跑用例较少时,可以手工添加指定的测试方法,但是测试方法较多且在多个不同的类时,手工添加就比较头大,有必要自动生成指定测试方法。

想着有2种方法:1.JUnitCore添加监听器RunListener,获得第一次执行的结果,根据结果聚合出指定的测试方法;2.通过扫结果日志,找到FAILED和ERROR,聚合出指定的测试方法。由于项目中是通过ant执行junit和junit-report获得第一次运行结果,自己添加监听器RunListener,不好使,已经尝试过,不成功。如果你是通过JUnitCore运行第一次,可以通过添加监听器RunListener获得FAILED和ERROR。下面介绍具体实现。

二.根据结果日志,自动生成测试类

1.在执行ant时,在build.xml中target:junit中添加<formatter type="brief" usefile="true" />,这样会在目标目录下,生成失败用例日志,类似TEST-com.weibo.cases.suite.HugangTestSuite.txt这种的*.txt文件,根据该文件找到失败的测试方法,

FindFailTest.java,如下:

package com.weibo.failmethods;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
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.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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 {
		// TODO Auto-generated method stub

		// 文本每一行
		List<String> strList = new ArrayList();
		// 行号
		List<Integer> flags = new ArrayList();
		// 记录FAILED和ERROR
		Set<String> 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(System.getProperty("user.dir") + "/src/com/weibo/failmethods/" + 
					"TEST-com.weibo.cases.suite.HugangTestSuite.txt"));
			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<String, List<String>> myClassMethodMap = new LinkedHashMap();

		List<String> className = new ArrayList<String>();
		List<String> methodName = new ArrayList<String>();

		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<String> methodList = myClassMethodMap.get(classPart);
				methodList.add(methodPart);
				myClassMethodMap.put(classPart, methodList);
			} else {
				// 第一次添加该class时
				List<String> firstMethod = new ArrayList<String>();
				firstMethod.add(methodPart);
				myClassMethodMap.put(classPart, firstMethod);
			}

			// className.add(classPart);
			// methodName.add(methodPart);

			System.out.println(classPart + " " + methodPart);

		}

		System.out.println(className.size());

		System.out.println(methodName.size());

		System.out.println(myClassMethodMap);

		new WriteFailTest().writeJavaClass(myClassMethodMap);

		
		

	}

}
根据获得的失败用例, WriteFailTest.java自动生成测试文件AutoFailMethodsTest.java:

WriteFailTest.java如下:

package com.weibo.failmethods;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.junit.runner.JUnitCore;
import org.junit.runner.Request;
import org.junit.runner.Result;

import com.weibo.failmethods.FailMethodsTest;
import com.weibo.failmethods.SuiteClassesMethods;


/**
 * 
 * @author hugang
 * 直接生成文件
 */
public class WriteFailTest {
	
	private BufferedWriter writer;

	public void createClassAnnotation(Map<String, List<String>> myClassMethodMap) throws IOException {

		SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss,SSS");
		writer.write("//auto java file generated on: ");
		writer.write(sdf.format(new Date()));
		writer.newLine();
		writer.write("package com.weibo.failmethods;");
		writer.newLine();
		writer.write("import java.text.SimpleDateFormat;");
		writer.newLine();
		writer.write("import java.io.*;");
		writer.newLine();
		writer.write("import java.util.*;");
		writer.newLine();
		writer.write("import org.junit.runner.*;");
		writer.newLine();
		writer.write("import com.weibo.cases.hugang.*;");
		writer.newLine();
		writer.write("import com.weibo.cases.xiaoyu.*;");
		writer.newLine();
		writer.write("import com.weibo.cases.wanglei16.*;");
		writer.newLine();
		writer.write("import com.weibo.cases.xuelian.*;");
		writer.newLine();
		writer.write("import com.weibo.cases.lingna.*;");
		writer.newLine();
		writer.write("import com.weibo.cases.maincase.*;");
		writer.newLine();
		
		String className = "";
		String methodsName = "";
		String methodsNumPerClass = "";	
		
		// 拼接结果
		for(Map.Entry<String, List<String>> entry : myClassMethodMap.entrySet()){
            className += entry.getKey() + ",";
            List<String> failMethod = entry.getValue();
            for(int m = 0; m < failMethod.size(); m++){
            	methodsName += failMethod.get(m) + ",";
            }
            methodsNumPerClass += failMethod.size() + ",";
		}		
		
		System.out.println(className);
		System.out.println(methodsNumPerClass);
		System.out.println(methodsName);

		writer.newLine();
		writer.write("@SuiteClassesMethods(className = {");
		writer.newLine();
		// className.length()-1 去掉最后一个,
		writer.write(className.substring(0, className.length()-1) + "},");
		writer.newLine();
		writer.write("methodsNumPerClass = {");
		writer.newLine();
		writer.write(methodsNumPerClass.substring(0, methodsNumPerClass.length()-1) + "},");
		writer.newLine();
		writer.write("methodsName = {");
		writer.newLine();
		writer.write(methodsName.substring(0, methodsName.length()-1) + "})");

		
}
				
	public void createClassBody() throws IOException{
		
		writer.newLine();
		writer.write("public class AutoFailMethodsTest {");
		writer.newLine();
		writer.write("	public static void main(String[] args) throws Exception {");
		writer.newLine();
		writer.write("		Class<AutoFailMethodsTest> clazz = AutoFailMethodsTest.class;");
		writer.newLine();
		writer.write("		SuiteClassesMethods scm = clazz.getAnnotation(SuiteClassesMethods.class);");
		writer.newLine();
		writer.write("		Class<?>[] className = scm.className();");
		writer.newLine();
		writer.write("		int[] methodsNumPerClass = scm.methodsNumPerClass();");
		writer.newLine();
		writer.write("		String[] methodsName = scm.methodsName();");
		writer.newLine();
		writer.write("		Map<Class<?>, List<String>> myClassMethodMap = new LinkedHashMap();");
		writer.newLine();
		writer.write("		List<List<String>> listMethodsName = new ArrayList<List<String>>();");
		writer.newLine();
		writer.write("		int k = 0;");
		writer.newLine();
		writer.write("		for (int i = 0; i < className.length; i++) {");
		writer.newLine();
		writer.write("			List<String> temp = new ArrayList<String>();");
		writer.newLine();
		writer.write("			for (int m = 0; m < methodsNumPerClass[i]; m++) {");
		writer.newLine();
		writer.write("		 		temp.add(methodsName[k]);");
		writer.newLine();
		writer.write("		 		k++;");
		writer.newLine();
		writer.write(" 			}");
		writer.newLine();
		writer.write("		listMethodsName.add(i, temp);");
		writer.newLine();
		writer.write("	 	myClassMethodMap.put(className[i], listMethodsName.get(i));");
		writer.newLine();
		writer.write(" 		}");
		writer.newLine();
		writer.write(" 		JUnitCore junitRunner = new JUnitCore();");
		writer.newLine();
		writer.write("		List<Result> methodsResult = new ArrayList<Result>();");
		writer.newLine();
		writer.write("		int failNum = 0;");
		writer.newLine();
		writer.write("		int successNum = 0;");
		writer.newLine();
		writer.write("		long runTime = 0L;");
		writer.newLine();
		writer.write("		for(Map.Entry<Class<?>, List<String>> entry : myClassMethodMap.entrySet()){");
		writer.newLine();
		writer.write("			Class testClass = entry.getKey();");
		writer.newLine();
		writer.write("			List<String> failMethod = entry.getValue();");
		writer.newLine();
		writer.write("			for(int i = 0; i < failMethod.size(); i++){");
		writer.newLine();
		writer.write("				Request request = Request.method(testClass, failMethod.get(i));");
		writer.newLine();
		writer.write("				Result result = junitRunner.run(request);");
		writer.newLine();
		writer.write("              runTime += result.getRunTime();");
		writer.newLine();
		writer.write("				if(result.wasSuccessful()){");
		writer.newLine();
		writer.write("					successNum++;");
		writer.newLine();
		writer.write("				}else{");
		writer.newLine();
		writer.write("					failNum++;");
		writer.newLine();
		writer.write("					methodsResult.add(result);");
		writer.newLine();
		writer.write("				}");
		writer.newLine();
		writer.write("			}	");
		writer.newLine();
		writer.write("		}");
		writer.newLine();
		writer.write("		try{");
		writer.newLine();
		// 记录结果集 , 目录
		writer.write("			String filePath = System.getProperty(\"user.dir\") + \"/src/com/weibo/failmethods/Result.txt\";");
		writer.newLine();
		writer.write("			File file = new File(filePath);");
		writer.newLine();
		writer.write("			if(!file.exists()){");
		writer.newLine();
		writer.write("				file.createNewFile();");
		writer.newLine();
		writer.write("			}");
		writer.newLine();
		writer.write("			FileOutputStream fop = new FileOutputStream(file);");
		writer.newLine();
		
		writer.write("			SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy.MM.dd HH:mm:ss,SSS\");");
		writer.newLine();
		writer.write("			fop.write(\"## Second Time's Result generated on: \".getBytes());");
		writer.newLine();
		writer.write("			fop.write(sdf.format(new Date()).getBytes());");
		writer.newLine();
		writer.write("			fop.write(\"\\n\".getBytes());");
		writer.newLine();
		writer.write("			StringBuffer sb = new StringBuffer();");
		writer.newLine();
		writer.write("			sb.append(\"===================== 结果集 =====================\");");
		writer.newLine();
		writer.write("			sb.append(\"\\n\");");
		writer.newLine();
		writer.write("			sb.append(\"用例总数:\" + methodsName.length);");
		writer.newLine();
		writer.write("			sb.append(\", 成功数:\" + successNum);");
		writer.newLine();
		writer.write("			sb.append(\", 失败数:\" + failNum);");
		writer.newLine();
		writer.write("			sb.append(\", 运行时间:\" + (runTime/1000)/60 + \" 分钟 \" + (runTime/1000)%60 + \" 秒\");");
		writer.newLine();
		writer.write("			sb.append(\"\\n\");");
		writer.newLine();
		writer.write("			sb.append(\"=================================================\");");
		writer.newLine();
		writer.write("			sb.append(\"\\n\");");
		writer.newLine();
		writer.write("			sb.append(\"\\n\");");
		writer.newLine();
		writer.write("			fop.write(sb.toString().getBytes());");
		writer.newLine();
		writer.write("			for(int j = 0; j < methodsResult.size(); j++){");
		writer.newLine();
		writer.write("				byte[] fail = methodsResult.get(j).getFailures().toString().getBytes();");
		writer.newLine();
		writer.write(" 				fop.write(fail);");
		writer.newLine();
		writer.write(" 				fop.write(\"\\n\".getBytes());");
		writer.newLine();
		writer.write(" 				fop.write(\"\\n\".getBytes());");
		writer.newLine();
		writer.write(" 			}");
		writer.newLine();
		writer.write("			fop.flush();");
		writer.newLine();
		writer.write("			fop.close();");
		writer.newLine();
		writer.write(" 		}catch(IOException e){");
		writer.newLine();
		writer.write("		e.printStackTrace();");
		writer.newLine();
		writer.write("		}");
		writer.newLine();	
		writer.write("	}");
		writer.newLine();	
		writer.write("}");
		writer.newLine();	
		
	}
	

	

	public void writeJavaClass(Map<String, List<String>> myClassMethodMap) throws IOException {
		try {

			File sourceFile = new File(System.getProperty("user.dir") + "/src/com/weibo/failmethods/" + "AutoFailMethodsTest.java");
			if (!sourceFile.exists()) {
				sourceFile.createNewFile();
			}

			if (sourceFile.exists() && !sourceFile.delete()) {
				throw new IOException("could not delete " + sourceFile);
			}
			writer = new BufferedWriter(new FileWriter(sourceFile));

			createClassAnnotation(myClassMethodMap);
			createClassBody();

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			writer.close();
		}
	}
}
运行生成的文件: AutoFailMethodsTest.java

得到结果Result.txt:

JUnit4---实践二:拓展之运行指定类的某些测试方法-自动生成该文件_第1张图片


三.通过JUnitCore添加RunListener,获得失败信息

1.JUnitCore添加RunListener,代码如下:

<span style="font-family: Monaco;">package com.weibo.cases.hugang;</span>

import org.junit.Rule;
import org.junit.experimental.ParallelComputer;
import org.junit.experimental.max.MaxCore;
import org.junit.rules.ErrorCollector;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;

import com.weibo.cases.suite.LikeTestSuite;
import com.weibo.cases.suite.WangleiTestSuite;

public class RetryFailCaseTest {

	public static void main(String[] args) {
		
	JUnitCore core = new JUnitCore();
	core.addListener(new MyFailListener());
	Result rs = core.run(LikeTestSuite.class);
	
	System.out.println(rs.getRunCount() + " " + rs.getRunTime() + " " + rs.getFailureCount());
	
	}

}

MyFailListener类为RunListener类的子类,可以自定义一些操作,代码如下:

package com.weibo.cases.hugang;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Set;
import java.util.TreeSet;

import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;


public class MyFailListener extends RunListener {
	public void testFailure(Failure failure) throws IOException{

		
		File failFile = new File("myfail.txt");
		
		if(!failFile.exists()){
			failFile.createNewFile();
		}
		
		try {
			FileOutputStream fos = new FileOutputStream(failFile, true);
			fos.write((failure.toString() + "\n").getBytes());
			fos.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		
	}
}

自定义监听器MyFailListener将错误信息,写文件,文件内容类似于testObjectsDestroy(com.weibo.cases.xuelian.LikesListBatchLikeTest): object is null!

可以根据错误信息,聚合出失败文件,类似上一节“二.根据结果日志,自动生成测试类”,不再敖述。



你可能感兴趣的:(JUnit4---实践二:拓展之运行指定类的某些测试方法-自动生成该文件)