特别鸣谢博客链接:
jar包启动testng log冲突启动异常
前提环境,jdk8,使用jdk5会报错Could not initialize class org.codehaus.groovy.vmplugin.v7.Java7
springboot工程新建testng模块,项目结构如下。
package basetest;
import com.alibaba.fastjson.JSONObject;
import com.被测.ApplicationLauncher;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.context.WebApplicationContext;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* @Des: testng基础测试类
* 加载被测-web模块,yml指定启动参数
*/
@AutoConfigureMockMvc
@TestExecutionListeners(listeners = MockitoTestExecutionListener.class)
@SpringBootTest(classes = ApplicationLauncher.class)
@ActiveProfiles("mock")
@Slf4j
public class BaseTestNg extends AbstractTestNGSpringContextTests {
@Autowired
public WebApplicationContext applicationContext;
public MockMvc mvc;
@BeforeClass
public void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(applicationContext).build();
}
@Test
public void testBaidu(){
logger.info("Success!");
}
@Test
void query() throws Exception {
String id = "12345";
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders
.get("/queryOrder")
.param("orderId", id)
.contentType(MediaType.APPLICATION_JSON);
MvcResult mvcResult = mvc.perform(requestBuilder)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print())
.andReturn();
MockHttpServletResponse response = mvcResult.getResponse();
response.setCharacterEncoding("UTF-8");
log.info("响应状态:{} 响应内容:{}", response.getStatus(), response.getContentAsString());
}
}
package basetest;
import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.ResourceCDN;
import com.aventstack.extentreports.Status;
import com.aventstack.extentreports.model.TestAttribute;
import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
import com.aventstack.extentreports.reporter.configuration.ChartLocation;
import org.testng.*;
import org.testng.xml.XmlSuite;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @Des:监听报告实现
*/
public class ExtentTestNGIReporterListener implements IReporter {
//生成的路径以及文件名
private static final String OUTPUT_FOLDER = "/data/logs/testng/report/" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
// private static final String OUTPUT_FOLDER = "test-output/";
private static final String FILE_NAME = "index.html";
private ExtentReports extent;
@Override
public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
init();
boolean createSuiteNode = false;
if(suites.size()>1){
createSuiteNode=true;
}
for (ISuite suite : suites) {
Map<String, ISuiteResult> result = suite.getResults();
//如果suite里面没有任何用例,直接跳过,不在报告里生成
if(result.size()==0){
continue;
}
//统计suite下的成功、失败、跳过的总用例数
int suiteFailSize=0;
int suitePassSize=0;
int suiteSkipSize=0;
ExtentTest suiteTest=null;
//存在多个suite的情况下,在报告中将同一个一个suite的测试结果归为一类,创建一级节点。
if(createSuiteNode){
suiteTest = extent.createTest(suite.getName()).assignCategory(suite.getName());
}
boolean createSuiteResultNode = false;
if(result.size()>1){
createSuiteResultNode=true;
}
for (ISuiteResult r : result.values()) {
ExtentTest resultNode;
ITestContext context = r.getTestContext();
if(createSuiteResultNode){
//没有创建suite的情况下,将在SuiteResult的创建为一级节点,否则创建为suite的一个子节点。
if( null == suiteTest){
resultNode = extent.createTest(r.getTestContext().getName());
}else{
resultNode = suiteTest.createNode(r.getTestContext().getName());
}
}else{
resultNode = suiteTest;
}
if(resultNode != null){
resultNode.getModel().setName(suite.getName()+" : "+r.getTestContext().getName());
if(resultNode.getModel().hasCategory()){
resultNode.assignCategory(r.getTestContext().getName());
}else{
resultNode.assignCategory(suite.getName(),r.getTestContext().getName());
}
resultNode.getModel().setStartTime(r.getTestContext().getStartDate());
resultNode.getModel().setEndTime(r.getTestContext().getEndDate());
//统计SuiteResult下的数据
int passSize = r.getTestContext().getPassedTests().size();
int failSize = r.getTestContext().getFailedTests().size();
int skipSize = r.getTestContext().getSkippedTests().size();
suitePassSize += passSize;
suiteFailSize += failSize;
suiteSkipSize += skipSize;
if(failSize>0){
resultNode.getModel().setStatus(Status.FAIL);
}
resultNode.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;",passSize,failSize,skipSize));
}
buildTestNodes(resultNode,context.getFailedTests(), Status.FAIL);
buildTestNodes(resultNode,context.getSkippedTests(), Status.SKIP);
buildTestNodes(resultNode,context.getPassedTests(), Status.PASS);
}
if(suiteTest!= null){
suiteTest.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;",suitePassSize,suiteFailSize,suiteSkipSize));
if(suiteFailSize>0){
suiteTest.getModel().setStatus(Status.FAIL);
}
}
}
// for (String s : Reporter.getOutput()) {
// extent.setTestRunnerOutput(s);
// }
extent.flush();
}
private void init() {
//文件夹不存在的话进行创建
File reportDir= new File(OUTPUT_FOLDER);
if(!reportDir.exists()&& !reportDir .isDirectory()){
reportDir.mkdir();
}
ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(OUTPUT_FOLDER + FILE_NAME);
htmlReporter.config().setDocumentTitle("api自动化测试报告");
htmlReporter.config().setReportName("api自动化测试报告");
htmlReporter.config().setChartVisibilityOnOpen(true);
htmlReporter.config().setTestViewChartLocation(ChartLocation.TOP);
// htmlReporter.config().setTheme(Theme.STANDARD);
htmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS);
htmlReporter.config().setCSS(".node.level-1 ul{ display:none;} .node.level-1.active ul{display:block;}");
extent = new ExtentReports();
extent.attachReporter(htmlReporter);
extent.setReportUsesManualConfiguration(true);
}
private void buildTestNodes(ExtentTest extenttest,IResultMap tests, Status status) {
//存在父节点时,获取父节点的标签
String[] categories=new String[0];
if(extenttest != null ){
List<TestAttribute> categoryList = extenttest.getModel().getCategoryContext().getAll();
categories = new String[categoryList.size()];
for(int index=0;index<categoryList.size();index++){
categories[index] = categoryList.get(index).getName();
}
}
ExtentTest test;
if (tests.size() > 0) {
//调整用例排序,按时间排序
Set<ITestResult> treeSet = new TreeSet<ITestResult>(new Comparator<ITestResult>() {
@Override
public int compare(ITestResult o1, ITestResult o2) {
return o1.getStartMillis()<o2.getStartMillis()?-1:1;
}
});
treeSet.addAll(tests.getAllResults());
for (ITestResult result : treeSet) {
Object[] parameters = result.getParameters();
String name="";
//如果有参数,则使用参数的toString组合代替报告中的name
for(Object param:parameters){
name = name + param.toString();
}
if(name.length()>0){
if(name.length()>50){
name= name.substring(0,49)+"...";
}
}else{
name = result.getMethod().getMethodName();
}
if(extenttest==null){
test = extent.createTest(name);
}else{
//作为子节点进行创建时,设置同父节点的标签一致,便于报告检索。
test = extenttest.createNode(name).assignCategory(categories);
}
//test.getModel().setDescription(description.toString());
//test = extent.createTest(result.getMethod().getMethodName());
for (String group : result.getMethod().getGroups()){
test.assignCategory(group);
}
List<String> outputList = Reporter.getOutput(result);
for(String output:outputList){
//将用例的log输出报告中
test.debug(output);
}
if (result.getThrowable() != null) {
test.log(status, result.getThrowable());
}
else {
test.log(status, "Test " + status.toString().toLowerCase() + "ed");
}
test.getModel().setStartTime(getTime(result.getStartMillis()));
test.getModel().setEndTime(getTime(result.getEndMillis()));
}
}
}
private Date getTime(long millis) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(millis);
return calendar.getTime();
}
}
package .moudletest;
import basetest.BaseTestNg;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.util.HashMap;
import java.util.Map;
/**
* @Des:被测-web,mockmvc单元测试
*/
@Log4j2
public class TestController extends BaseTestNg {
String wishUnifiedOrderTemplateStr;
@BeforeMethod
void initTemplates(){
//预下单,请求模板
wishUnifiedOrderTemplateStr = "json";
}
@Test
void query() throws Exception {
String id = "127943770";
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders
.get("/queryOrder")
.param("orderId", id)
.contentType(MediaType.APPLICATION_JSON);
MvcResult mvcResult = mvc.perform(requestBuilder)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print())
.andReturn();
MockHttpServletResponse response = mvcResult.getResponse();
response.setCharacterEncoding("UTF-8");
log.info("响应状态:{} 响应内容:{}", response.getStatus(), response.getContentAsString());
}
}
package com.被测;
import lombok.extern.log4j.Log4j2;
import org.testng.TestNG;
/**
* @Des:testng模块启动入口
*/
@Log4j2
public class TestMain {
public static void main(String[] args) {
org.testng.TestNG.main(args);
}
}
<suite name="Suite" parallel="methods" thread-count="5">
<test name="Test">
<classes>
<class name="com.被测.moudletest.TestController">
<methods>
<include name="query">冒烟单元测试 查询订单状态include>
methods>
class>
classes>
test>
suite>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>主-parentartifactId>
<groupId>com.被测.platformgroupId>
<version>0.0.1-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>testngartifactId>
<packaging>jarpackaging>
<properties>
<start-class>com.被测.TestMainstart-class>
properties>
<dependencies>
<dependency>
<groupId>io.rest-assuredgroupId>
<artifactId>rest-assuredartifactId>
dependency>
<dependency>
<groupId>io.rest-assuredgroupId>
<artifactId>json-schema-validatorartifactId>
<version>3.0.2version>
dependency>
<dependency>
<groupId>io.rest-assuredgroupId>
<artifactId>spring-mock-mvcartifactId>
<version>3.0.6version>
dependency>
<dependency>
<groupId>org.hamcrestgroupId>
<artifactId>hamcrest-libraryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-loggingartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-log4j2artifactId>
dependency>
<dependency>
<groupId>com.被测.platformgroupId>
<artifactId>被测-webartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.testnggroupId>
<artifactId>testngartifactId>
<version>7.1.0version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-test-autoconfigureartifactId>
dependency>
<dependency>
<groupId>com.aventstackgroupId>
<artifactId>extentreportsartifactId>
<version>3.0.3version>
dependency>
dependencies>
<build>
<finalName>包名finalName>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.*include>
includes>
resource>
resources>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-failsafe-pluginartifactId>
<version>3.0.0-M4version>
<executions>
<execution>
<id>default-integration-testid>
<goals>
<goal>integration-testgoal>
goals>
<configuration>
<excludes>
<exclude>noneexclude>
excludes>
<includes>
<include>**/*Test.javainclude>
includes>
configuration>
execution>
executions>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.1version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-surefire-pluginartifactId>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>src/main/resources/testng.xmlsuiteXmlFile>
suiteXmlFiles>
<properties>
<property>
<name>usedefaultlistenersname>
<value>falsevalue>
property>
<property>
<name>listenername>
<value>org.uncommons.reportng.HTMLReporter,
org.uncommons.reportng.JUnitXMLReporter
value>
property>
properties>
<forkMode>alwaysforkMode>
configuration>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-shade-pluginartifactId>
<version>3.2.1version>
<executions>
<execution>
<phase>packagephase>
<goals>
<goal>shadegoal>
goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlersresource>
transformer>
<transformer
implementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer">
<resource>META-INF/spring.factoriesresource>
transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemasresource>
transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>${start-class}mainClass>
transformer>
transformers>
configuration>
execution>
executions>
plugin>
plugins>
build>
project>
使用mvn clean package打包,把包名.jar复制到新文件夹,使用命令启动。
java -jar 包名.jar -testjar 包名.jar
若有多个xml,可以通过外部指定jar内xml路径,运行命令如下。
java -jar 包名.jar -xmlpathinjar baseSuite.xml -testjar 包名.jar
org.testng.TestNG命令参考如下:
You need to specify at least one testng.xml, one class or one method
Usage: <main class> [options] The XML suite files to run
Options:
-configfailurepolicy Configuration failure policy (skip or
continue)
-d Output directory
-dataproviderthreadcount Number of threads to use when running
data providers
-excludegroups Comma-separated list of group names to
exclude
-groups Comma-separated list of group names to be
run
-junit JUnit mode
Default: false
-listener List of .class files or list of class
names implementing ITestListener or
ISuiteListener
-methods Comma separated of test methods
Default: []
-methodselectors List of .class files or list of class
names implementing IMethodSelector
-mixed Mixed mode - autodetect the type of
current test and run it with appropriate runner
Default: false
-objectfactory List of .class files or list of class
names implementing ITestRunnerFactory
-parallel Parallel mode (methods, tests or classes)
-port The port
-reporter Extended configuration for custom report
listener
-suitename Default name of test suite, if not
specified in suite definition file or source code
-suitethreadpoolsize Size of the thread pool to use to run
suites
Default: 1
-testclass The list of test classes
-testjar A jar file containing the tests
-testname Default name of test, if not specified in
suitedefinition file or source code
-testnames The list of test names to run
-testrunfactory, -testRunFactory The factory used to create tests
-threadcount Number of threads to use when running
tests in parallel
-usedefaultlisteners Whether to use the default listeners
Default: true
-log, -verbose Level of verbosity
-xmlpathinjar The full path to the xml file inside the
jar file (only valid if -testjar was
specified)
Default: testng.xml