testng生成自定义html报告

testng原生的或reportng的报告总有些不符合需要,尝试生成自定义测试报告,
用到的依赖包:testng-6.9.9.jar,velocity-1.7.jar


1.定义一个DataBean,保存需要收集的数据

只定义部分数据,比如suite、testname、groups等好多数据还没,需要用到的时候再加了

package com.reporter.main;

import java.util.Collection;
import java.util.List;

import org.testng.ITestNGMethod;

public class DataBean {
	private int excludeTestsSize; //未执行的test数量
	private int passedTestsSize; //测试通过的数量
	private int failedTestsSize; //测试失败的数量
	private int skippedTestsSize; //测试跳过的数量
	private int allTestsSize; //全部执行的测试的数量
	private ITestNGMethod[] allTestsMethod; //全部执行的测试方法
	private Collection excludeTestsMethod; //未执行的测试方法
	private String testsTime; //测试耗时
	private String passPercent; //测试通过率
	private String testName; //测试方法名
	private String className; //测试类名
	private String duration; //单个测试周期
	private String params; //测试用参数
	private String description; //测试描述
	private List output; //Reporter Output
	private String dependMethod; //测试依赖方法
	private Throwable throwable; //测试异常原因
	private StackTraceElement[] stackTrace; // 异常堆栈信息

	public int getExcludeTestsSize() {
		return excludeTestsSize;
	}

	public void setExcludeTestsSize(int excludeTestsSize) {
		this.excludeTestsSize = excludeTestsSize;
	}

	public int getPassedTestsSize() {
		return passedTestsSize;
	}

	public void setPassedTestsSize(int passedTestsSize) {
		this.passedTestsSize = passedTestsSize;
	}

	public int getFailedTestsSize() {
		return failedTestsSize;
	}

	public void setFailedTestsSize(int failedTestsSize) {
		this.failedTestsSize = failedTestsSize;
	}

	public int getSkippedTestsSize() {
		return skippedTestsSize;
	}

	public void setSkippedTestsSize(int skippedTestsSize) {
		this.skippedTestsSize = skippedTestsSize;
	}

	public int getAllTestsSize() {
		return allTestsSize;
	}

	public void setAllTestsSize(int allTestsSize) {
		this.allTestsSize = allTestsSize;
	}

	public String getPassPercent() {
		return passPercent;
	}

	public void setPassPercent(String passPercent) {
		this.passPercent = passPercent;
	}

	public String getTestName() {
		return testName;
	}

	public void setTestName(String testName) {
		this.testName = testName;
	}

	public String getClassName() {
		return className;
	}

	public void setClassName(String className) {
		this.className = className;
	}

	public String getDuration() {
		return duration;
	}

	public void setDuration(String duration) {
		this.duration = duration;
	}

	public String getParams() {
		return params;
	}

	public void setParams(String params) {
		this.params = params;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public List getOutput() {
		return output;
	}

	public void setOutput(List output) {
		this.output = output;
	}

	public String getDependMethod() {
		return dependMethod;
	}

	public void setDependMethod(String dependMethod) {
		this.dependMethod = dependMethod;
	}

	public Throwable getThrowable() {
		return throwable;
	}

	public void setThrowable(Throwable throwable2) {
		this.throwable = throwable2;
	}

	public StackTraceElement[] getStackTrace() {
		return stackTrace;
	}

	public void setStackTrace(StackTraceElement[] stackTrace) {
		this.stackTrace = stackTrace;
	}

	public void setTestsTime(String testsTime) {
		this.testsTime = testsTime;
	}

	public String getTestsTime() {
		return testsTime;
	}

	public void setAllTestsMethod(ITestNGMethod[] allTestsMethod) {
		this.allTestsMethod = allTestsMethod;
	}

	public ITestNGMethod[] getAllTestsMethod() {
		return allTestsMethod;
	}

	public void setExcludeTestsMethod(Collection excludeTestsMethod) {
		this.excludeTestsMethod = excludeTestsMethod;
	}

	public Collection getExcludeTestsMethod() {
		return excludeTestsMethod;
	}

}

2.对需要特别处理的报告元素,比如测试周期、通过率

package com.reporter.main;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.Reporter;

public class ReportUnits {
    private static final NumberFormat DURATION_FORMAT = new DecimalFormat("#0.000");
    private static final NumberFormat PERCENTAGE_FORMAT = new DecimalFormat("#0.00%");
	/**
	 *测试消耗时长 
	 *return 秒,保留3位小数
	 */
	public String getTestDuration(ITestContext context){
		long duration;
		duration=context.getEndDate().getTime()-context.getStartDate().getTime();
		return formatDuration(duration);
	}
	
    public String formatDuration(long elapsed)
    {
        double seconds = (double) elapsed / 1000;
        return DURATION_FORMAT.format(seconds);
    }
	/**
	 *测试通过率 
	 *return 2.22%,保留2位小数
	 */
    public String formatPercentage(int numerator, int denominator)
    {
        return PERCENTAGE_FORMAT.format(numerator / (double) denominator);
    }
    
    /**
     * 获取方法参数,以逗号分隔
     * @param result
     * @return
     */
    public String getParams(ITestResult result){
    	Object[] params = result.getParameters();
    	List list = new ArrayList(params.length);
    	for (Object o:params){
    		list.add(renderArgument(o));
    	}    	
    	return  commaSeparate(list);    	
    }
    /**
     * 获取依赖的方法
     * @param result
     * @return
     */
    public String getDependMethods(ITestResult result){
    	String[] methods=result.getMethod().getMethodsDependedUpon();
    	return commaSeparate(Arrays.asList(methods));
    }
    /**
     * 堆栈轨迹,暂不确定怎么做,放着先
     * @param throwable
     * @return
     */
    public String getCause(Throwable throwable){    	
    	StackTraceElement[] stackTrace=throwable.getStackTrace(); //堆栈轨迹
    	List list = new ArrayList(stackTrace.length);
    	for (Object o:stackTrace){
    		list.add(renderArgument(o));
    	}    	
    	return  commaSeparate(list);    	
    }
    /**
     * 获取全部日志输出信息
     * @return
     */
    public List getAllOutput(){
    	return Reporter.getOutput();
    }
    
    /**
     * 按testresult获取日志输出信息
     * @param result
     * @return
     */
    public List getTestOutput(ITestResult result){
    	return Reporter.getOutput(result);
    }
    

    /*将object 转换为String*/
    private String renderArgument(Object argument)
    {
        if (argument == null)
        {
            return "null";
        }
        else if (argument instanceof String)
        {
            return "\"" + argument + "\"";
        }
        else if (argument instanceof Character)
        {
            return "\'" + argument + "\'";
        }
        else
        {
            return argument.toString();
        }
    }
    /*将集合转换为以逗号分隔的字符串*/
    private String commaSeparate(Collection strings)
    {
        StringBuilder buffer = new StringBuilder();
        Iterator iterator = strings.iterator();
        while (iterator.hasNext())
        {
            String string = iterator.next();
            buffer.append(string);
            if (iterator.hasNext())
            {
                buffer.append(", ");
            }
        }
        return buffer.toString();
    }
}


3.测试方法排序,按测试方法执行时间排序
遍历 suites 得到的getAllResults()是一个set 集合,需要对数据进行排序
这里是将getAllResults()转为list,实现Comparable接口的方法进行排序的.

(好像是可以将getAllResults()转成TreeSet排序?)

package com.reporter.main;

import org.testng.ITestResult;

public class TestResultSort implements Comparable {
	private Long order;
	@Override
	public int compareTo(ITestResult arg0) {
		// TODO Auto-generated method stub
		return this.order.compareTo( arg0.getStartMillis());//按test开始时间排序
	}

}


4.得到测试报告数据

package org.reporter.main;


import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;


import org.testng.IResultMap;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.ITestResult;
import org.testng.Reporter;




public class ReporterData {
	// 测试结果Set转为list,再按执行时间排序 ,返回list
	public List sortByTime(Set str) {
		List list = new ArrayList();
		for (ITestResult r : str) {
			list.add(r);
		}
		Collections.sort(list);
		return list;


	}
	
	public DataBean testContext(ITestContext context) {
		// 测试结果汇总数据
		DataBean data = new DataBean();
		ReportUnits units = new ReportUnits();
		IResultMap passedTests = context.getPassedTests();
		IResultMap failedTests= context.getFailedTests();
		IResultMap skipedTests = context.getSkippedTests();	
		//全部测试周期方法,包括beforetest,beforeclass,beforemethod,aftertest,afterclass,aftermethod
		//IResultMap passedConfigurations =context.getPassedConfigurations(); 
		//IResultMap failedConfigurations =context.getFailedConfigurations();
		//IResultMap skipedConfigurations =context.getSkippedConfigurations();
		Collection excludeTests = context.getExcludedMethods();		
		
		int passedTestsSize = passedTests.size();
		int failedTestsSize = failedTests.size();
		int skipedTestsSize = skipedTests.size();
		int excludeTestsSize = excludeTests.size();
		//所有测试结果的数量=测试pass+fail+skip的和,因为数据驱动一个测试方法有多次执行的可能,导致方法总数并不等于测试总数
		int allTestsSize= passedTestsSize+failedTestsSize+skipedTestsSize;
		data.setAllTestsSize(allTestsSize);
		data.setPassedTestsSize(passedTestsSize);
		data.setFailedTestsSize(failedTestsSize);
		data.setSkippedTestsSize(skipedTestsSize);
		data.setExcludeTestsSize(excludeTestsSize);
		data.setTestsTime(units.getTestDuration(context));		
		data.setPassPercent(units.formatPercentage(passedTestsSize, allTestsSize));		
		data.setAllTestsMethod(context.getAllTestMethods());
		data.setExcludeTestsMethod(context.getExcludedMethods());
		
		return data;


	}


	public List testResults(IResultMap map, int status) {
		// 测试结果详细数据
		List list = new ArrayList();
		ReportUnits units = new ReportUnits();
		map.getAllResults().size();
		for (ITestResult result : sortByTime(map.getAllResults())) {
			DataBean data = new DataBean();
			data.setTestName(result.getName());
			data.setClassName(result.getTestClass().getName());
			data.setDuration(units.formatDuration(result.getEndMillis()
					- result.getStartMillis()));
			data.setParams(units.getParams(result));
			data.setDescription(result.getMethod().getDescription());
			data.setOutput(Reporter.getOutput(result));
			data.setDependMethod(units.getDependMethods(result));
			data.setThrowable(result.getThrowable());
			if (result.getThrowable() != null) {
				data.setStackTrace(result.getThrowable().getStackTrace());
			}
			list.add(data);
		}
		return list;
	}

}



5.生成测试报告,生成的测试报告是项目根目录下的report.html(要定制的话再改了)
使用 IReporter 监听器。IReporter 监听器只有一个方法需要实现。
void generateReport(java.util.List xmlSuites, java.util.List
suites, java.lang.String outputDirectory)

该方法在所有测试方法执行结束后被调用,通过遍历 xmlSuites 和 suites 能够获取所有测试方法的信息以及测试结果。outputDirectory 是默认的测试报表生成路径,当然你可以指定其他路径生成报表。

package com.reporter.main;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.Writer;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.testng.IReporter;
import org.testng.IResultMap;
import org.testng.ISuite;
import org.testng.ISuiteResult;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.xml.XmlSuite;

public class GenerateReporter implements IReporter {
	@Override
	public void generateReport(List xmlSuites, List suites,
			String outputDirectory) {
		// TODO Auto-generated method stub			
		try {
			// 初始化并取得Velocity引擎	
			VelocityEngine ve = new VelocityEngine();
			Properties p = new Properties();
			//虽然不懂为什么这样设置,但结果是好的.可以用了
			p.setProperty("resource.loader", "class");			 
			p.setProperty("class.resource.loader.class","org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
			ve.init(p);	
			Template t = ve.getTemplate("com/reporter/VMmodel/overview.vm");
			VelocityContext context = new VelocityContext();

			for (ISuite suite : suites) {
				Map suiteResults = suite.getResults();
				for (ISuiteResult suiteResult : suiteResults.values()) {
					ReporterData data = new ReporterData();					
					ITestContext testContext = suiteResult.getTestContext();
					// 把数据填入上下文
					context.put("overView", data.testContext(testContext));//测试结果汇总信息
					//ITestNGMethod[] allTests = testContext.getAllTestMethods();//所有的测试方法 
					//Collection excludeTests = testContext.getExcludedMethods();//未执行的测试方法
					IResultMap passedTests = testContext.getPassedTests();//测试通过的测试方法
	                IResultMap failedTests = testContext.getFailedTests();//测试失败的测试方法
	                IResultMap skippedTests = testContext.getSkippedTests();//测试跳过的测试方法
	   
	                context.put("pass", data.testResults(passedTests, ITestResult.SUCCESS));
	                context.put("fail", data.testResults(failedTests, ITestResult.FAILURE));
	                context.put("skip", data.testResults(skippedTests, ITestResult.FAILURE));
	                
					
						
				}
			}
			// 输出流
			//Writer writer = new BufferedWriter(new FileWriter("report.html"));
			OutputStream out=new FileOutputStream("report.html");
			Writer writer = new BufferedWriter(new OutputStreamWriter(out,"utf-8"));//解决乱码问题
			// 转换输出
			t.merge(context, writer);
			//System.out.println(writer.toString());
			writer.flush();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	

}


6.测试报告模板,文件后辍为vm,比如overview.vm,内容差不多就是一个html文件,(要漂亮报告找前端。。。)





  test
  
  
  



Test

OverView........
aaa
  all excluded passed faild skipped duration(S) passration alltestMethod excluedMethod
TestResult $overView.allTestsSize $overView.excludeTestsSize $overView.passedTestsSize $overView.failedTestsSize $overView.skippedTestsSize $overView.testsTime $overView.passPercent #foreach($p in $overView.allTestsMethod) $p
#end
#foreach($e in $overView.excludeTestsMethod) $e
#end


#foreach( $p in $pass) #end
PassTests.............
aaa
  testName className duration params description output dependMethod
$velocityCount ${p.testName} #if(${p.description}) (${p.description}) #end $p.className $p.duration $!p.params $!p.description #foreach($o in $p.output) $o
#end
$p.dependMethod $!p.throwable #if($p.throwable ) #foreach($o in $p.stackTrace) $o
#end #end



#foreach( $p in $fail) #end
FailedTests...............
aaa
  testName className duration params description output dependMethod throwable stackTrace
$velocityCount $p.testName $p.className $p.duration $!p.params $!p.description #foreach($o in $p.output) $o
#end
$p.dependMethod $p.throwable #if($p.throwable ) #foreach($o in $p.stackTrace) $o
#end #end




7.测试报告,新建一个java项目,在测试类中添加监听器
@Listeners({com.reporter.main.GenerateReporter.class})
或者在testng.xml添加监听器

import org.testng.Assert;
import org.testng.Reporter;
import org.testng.annotations.Listeners;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;

@Listeners({com.reporter.main.GenerateReporter.class})
public class test {
	@Test
	public void a(){
		Reporter.log("baidu.com");
		System.out.println("111");
	}
	@Test(enabled=false,dependsOnMethods="a")
	@Parameters("param")
	public void b(String s){
		Assert.assertEquals(s,"dataxml");
		
	}
	@Test(enabled=true,dependsOnMethods="e")
	public void c(){		
		Assert.assertEquals(2,2);
	}
	@Test(description="测试方法 DDD")
	public void d() {
		Reporter.log("DDDDDDDDDD");
		Reporter.log("AAAAAAAAAAAA");
		System.out.println("Verify.verifyEquals(2,2)");
		 Assert.assertEquals(2,2);
	}
	@Test(description="98788",groups="test",invocationCount=1,dependsOnMethods="d")
	public void e() {
		Reporter.log("EEEEEEEEEEEEEEEEE");
		Assert.assertEquals(1,2);
		System.out.println("Verify.verifyEquals(2,2)");
	}
}


你可能感兴趣的:(testng生成自定义html报告)