TestNg+Springboot打包jar命令运行

特别鸣谢博客链接:
jar包启动testng log冲突启动异常

前提环境,jdk8,使用jdk5会报错Could not initialize class org.codehaus.groovy.vmplugin.v7.Java7

1、创建module。

springboot工程新建testng模块,项目结构如下。
TestNg+Springboot打包jar命令运行_第1张图片

2、基础代码。

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>

3、打包运行。

使用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

4、问题总结

  1. 运行命令的第一个jar会启动被测web项目,但是缺少yml文件会报错,随即启动testng模块运行xml执行相应test,之后会在jar包所在目录生成报告。
  2. 目前testng.xml会随mvn编译进jar包,无法从外部动态指定维护的testng.xml所以每次修改xml需要新编译,待解决。

你可能感兴趣的:(工具环境,java,白箱测试)