JUnit4---实践三:支持多线程,失败重试执行测试case的方法

一.背景

多线程执行用例能很大的节约时间,而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,生成报告,并生成错误日志;根据扫日志,找出失败用例。

1.FindFailTest:扫日志,生成执行失败用例的文件

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<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(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<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);
			}

		
			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);

	}
}

2.生成执行文件:

AutoFailMethodsTest.java,实现执行结果显示和将本次失败的结果回写到元日志TEST-com.weibo.cases.suite.WangleiTestSuite.txt,实现只跑上一次失败的用例

//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<AutoFailMethodsTest> clazz = AutoFailMethodsTest.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>>();
		int k = 0;
		for (int i = 0; i < className.length; i++) {
			List<String> temp = new ArrayList<String>();
			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<Result> methodsResult = new ArrayList<Result>();
		int failNum = 0;
		int successNum = 0;
		long runTime = 0L;
		ExecutorService executorService = Executors.newFixedThreadPool(THREADCOUNT);
		List<Future<Result>> listFr = new ArrayList<Future<Result>>();
		long startTime = System.currentTimeMillis();
		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++){
				Future<Result> fr = executorService.submit(new ThreadRunTest(testClass, failMethod.get(i)));
				listFr.add(fr);
	}
}
for(Future<Result> 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;生成只执行第一步没过的用例的结果:


依次类推...


代码已 重构, 请参见:JUnit结果重跑失败用例(支持Mvn和Ant)




  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
powermock-module-junit4-common是PowerMock框架中的一个模块,用于与JUnit4集成。这个模块可以在测试过程中模拟和修改静态方法、私有方法、构造函数和final类等。当我们需要对这些难以测试的代码进行单元测试时,可以使用这个模块来解决这个问题。 要下载powermock-module-junit4-common,需要先确保在项目的构建工具中引入了PowerMock框架的依赖。如果使用Maven进行构建,可以在pom.xml文件中添加以下依赖: <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4-common</artifactId> <version>版本号</version> <scope>test</scope> </dependency> 在Gradle中,可以在build.gradle文件的dependencies部分添加以下代码: testImplementation 'org.powermock:powermock-module-junit4-common:版本号' 在配置好依赖之后,再执行构建工具的相关命令,即可自动下载并引入powermock-module-junit4-common模块。 通过使用powermock-module-junit4-common,我们可以更方便地编写单元测试,尤其适用于需要模拟静态方法、私有方法或者处理final类的情况。它为我们提供了更多的测试可能性,使得我们能够更全面地覆盖代码并提高测试覆盖率。同时,它也提供了一系列好用的工具类和注解,让测试代码编写更加简洁高效。 总之,下载并使用powermock-module-junit4-common模块可以为我们提供更强大的测试能力,帮助我们更好地进行单元测试

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值