当前位置: 代码迷 >> 综合 >> 全网最靠谱的SpringBoot+TestNG+Maven打包成jar运行单元测试
  详细解决方案

全网最靠谱的SpringBoot+TestNG+Maven打包成jar运行单元测试

热度:73   发布时间:2023-11-07 17:15:07.0

认识自己的无知是认识世界最可靠的方法————互联网中的无名之辈

背景

公司最的海外项目临近上线,国内所有的环境都进行了单元测试,唯有海外环境还没进行,所以公司需要在海外环境进行单元测试。公司使用的是SpringBoot+TestNG写的单元测试,单元测试中去调用了其他微服务的Service。在国内环境只需要在本地或者jenkins上执行测试命令,比如通过mvn test 或者在IDEA里面直接执行对应的testng.mxl文件。

由于海外环境网络的限制和服务的限制公司需要将SpringBoot+TestNG打成jar包的形式去执行。不巧这个任务分配到我这了,我一听,哦,就是打个jar包跑测试啊,那岂不是洒洒水。

在这里插入图片描述

初探

坑1

我之前遇见的打包都是把单元测试排除的,现在的需求是将测试打包进去于是我就去网上找了找例子。嗯很快就找到了下面这篇博客.。

https://blog.csdn.net/qq_15290529/article/details/78989234

其实写的挺好的,我照做了之后发现问题来了,打包执行的时候需要有程序入口,现在的项目的有一个标准的SpringBoot Application入口,我想都没想就把它设置成了入口,不一会儿我的jar包就完成了。

后面我怀着不屑运行了java -jar 启动了程序,不出意外Application正常启动,等了等,嗯…嗯?不对劲,猛地意识到这个程序的入口有问题,入口应该设置成一个能启动所有TestNG测试的方法。

坑2

于是我开始思考怎么在Application启动的时候去执行单元测试呢?
不一会儿,我看了下面这篇博客之后有思路了.

http://www.voidcn.com/article/p-yehsfrxe-btn.html

我写了一个TestStartService 继承了ApplicationRunner,重写了run方法,我想的是这样能够在启动Application的时候执行单元测试。

@Component
@Order(value = 1)
public class TestStartService  implements ApplicationRunner {
    @AutowiredTestNG testNG;@Overridepublic void run(ApplicationArguments applicationArguments) throws Exception {
    List<String> suites = new ArrayList<String>();suites.add("src/main/resources/testng.xml");testNG.setTestSuites(suites);testNG.run();}
}

测试立马就报错了

Error starting ApplicationContext.

通过DEBUG发现确实是在容器启动的时候去加载了testng.xml,也去找了对应测试类,因为我们的测试类注入了其他系统的Service,但是这样的方式去执行测试的时候不会去加载上下文环境。我又去网上查了一波资料,看到 stackoverflow 上有个一样的需求,但是没人回答,我越来越觉得这个任务是个坑了。

https://stackoverflow.com/questions/63934534/how-to-create-fat-jar-with-tests-sources-and-all-dependencies-on-spring-boot-app

后来跑去读了SpringBoot官方文档、读了TestNG API文档、写了几个自己猜想的idea ,但是任还是失败告终。值得一提的是有一个类在我看到所以资料里面出现率非常高,我先留了个印象,先看看这个类,埋个伏笔。

  org.testng.TestNG

最后的挣扎

这已经是第二天的事情了,昨日几乎找遍了全网有关TestNG的资料,尝试了我能想到的所有方法。距离任务结束就只有8小时左右了,作为刚刚入职的应届生(而且还没过试用期),我真的慌了,甚至都联想了卷包袱走人的场面。

现在该求的人都求了,该找的额资料都找了,唯有源码还没看,于是我就沉下心思看来看源码,因为上面提到的类出现频率最高,所以我就直接从这个类进去开始阅读源码。

  org.testng.TestNG

看到下面这段代码我顿时沸腾了

public static void main(String[] argv) {
    TestNG testng = privateMain(argv, (ITestListener)null);System.exit(testng.getStatus());}
    @Deprecatedpublic void configure(Map cmdLineArgs) {
    CommandLineArgs result = new CommandLineArgs();Integer verbose = (Integer)cmdLineArgs.get("-log");if (null != verbose) {
    result.verbose = verbose;}result.outputDirectory = (String)cmdLineArgs.get("-d");String testClasses = (String)cmdLineArgs.get("-testclass");if (null != testClasses) {
    result.testClass = testClasses;}String testNames = (String)cmdLineArgs.get("-testnames");if (testNames != null) {
    result.testNames = testNames;}String useDefaultListeners = (String)cmdLineArgs.get("-usedefaultlisteners");if (null != useDefaultListeners) {
    result.useDefaultListeners = useDefaultListeners;}result.groups = (String)cmdLineArgs.get("-groups");result.excludedGroups = (String)cmdLineArgs.get("-excludegroups");result.testJar = (String)cmdLineArgs.get("-testjar");result.xmlPathInJar = (String)cmdLineArgs.get("-xmlpathinjar");result.junit = (Boolean)cmdLineArgs.get("-junit");result.mixed = (Boolean)cmdLineArgs.get("-mixed");result.master = (String)cmdLineArgs.get("-master");result.slave = (String)cmdLineArgs.get("-slave");result.skipFailedInvocationCounts = (Boolean)cmdLineArgs.get("-skipfailedinvocationcounts");String parallelMode = (String)cmdLineArgs.get("-parallel");if (parallelMode != null) {
    result.parallelMode = parallelMode;}String threadCount = (String)cmdLineArgs.get("-threadcount");if (threadCount != null) {
    result.threadCount = Integer.parseInt(threadCount);}Integer dptc = (Integer)cmdLineArgs.get("-dataproviderthreadcount");if (dptc != null) {
    result.dataProviderThreadCount = dptc;}String defaultSuiteName = (String)cmdLineArgs.get("-suitename");if (defaultSuiteName != null) {
    result.suiteName = defaultSuiteName;}String defaultTestName = (String)cmdLineArgs.get("-testname");if (defaultTestName != null) {
    result.testName = defaultTestName;}Object listeners = cmdLineArgs.get("-listener");if (listeners instanceof List) {
    result.listener = Utils.joinClasses((List)listeners, ",");} else {
    result.listener = (String)listeners;}String ms = (String)cmdLineArgs.get("-methodselectors");if (null != ms) {
    result.methodSelectors = ms;}String objectFactory = (String)cmdLineArgs.get("-objectfactory");if (null != objectFactory) {
    result.objectFactory = objectFactory;}String runnerFactory = (String)cmdLineArgs.get("-testrunfactory");if (null != runnerFactory) {
    result.testRunnerFactory = runnerFactory;}String reporterConfigs = (String)cmdLineArgs.get("-reporter");if (reporterConfigs != null) {
    result.reporter = reporterConfigs;}String failurePolicy = (String)cmdLineArgs.get("-configfailurepolicy");if (failurePolicy != null) {
    result.configFailurePolicy = failurePolicy;}this.configure(result);}

看见main方法,再结合出现的configure,我就联想到了shell 命令,通过 -【options】 xxx 就能设置对应的参数,我单独起了个程序写了下面这段代码。

public class TestNGMain {
    public static void main(String[] args) {
    org.testng.TestNG.main(args);}
}

将程序打成jar,看见下面的内容,我就感觉我距离出坑不远了

You need to specify at least one testng.xml, one class or one method
Usage: <main class> [options] The XML suite files to runOptions:-configfailurepolicy               Configuration failure policy (skip orcontinue)-d                                 Output directory-dataproviderthreadcount           Number of threads to use when runningdata providers-excludegroups                     Comma-separated list of group names toexclude-groups                            Comma-separated list of group names to berun-junit                             JUnit modeDefault: false-listener                          List of .class files or list of classnames implementing ITestListener orISuiteListener-methods                           Comma separated of test methodsDefault: []-methodselectors                   List of .class files or list of classnames implementing IMethodSelector-mixed                             Mixed mode - autodetect the type ofcurrent test and run it with appropriate runnerDefault: false-objectfactory                     List of .class files or list of classnames implementing ITestRunnerFactory-parallel                          Parallel mode (methods, tests or classes)-port                              The port-reporter                          Extended configuration for custom reportlistener-suitename                         Default name of test suite, if notspecified in suite definition file or source code-suitethreadpoolsize               Size of the thread pool to use to runsuitesDefault: 1-testclass                         The list of test classes-testjar                           A jar file containing the tests-testname                          Default name of test, if not specified insuitedefinition 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 runningtests in parallel-usedefaultlisteners               Whether to use the default listenersDefault: true-log, -verbose                     Level of verbosity-xmlpathinjar                      The full path to the xml file inside thejar file (only valid if -testjar wasspecified)Default: testng.xml

于是我将之前的maven打包配置改成了上面这段代码。立马大了jar包
根据上面的命令提示我执行了下面的代码

java -jar 包名.jar -testjar 包名.jar

nice 我出坑了,ohhhhhhhhhhhhhhhhhhhhhhhhhh。

因为默认的suite就是testng.xml 和我的情况符合,所以我就没去设置 -xmlpathinjar 参数,如果情况不同的需要进行设置,这个是最重要的!

关键点

大概的看看我的pom配置和代码,因为上问的博客其实还是存在点差异的,这里我该踩的坑都踩了放心食用!

<?xml version="1.0" encoding="UTF-8"?>
<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"><packaging>jar</packaging><properties><!-- 上文提到的TestNG入口函数--><start-class>org.kuohai.TestNGMain</start-class></properties><dependencies><dependency><groupId>org.uncommons</groupId><artifactId>reportng</artifactId><version>1.1.4</version></dependency><dependency><groupId>org.testng</groupId><artifactId>testng</artifactId><version>6.8</version></dependency></dependencies><build><finalName>包名</finalName><resources><resource><directory>src/main/resources</directory><includes><!--包含文件夹以及子文件夹下所有资源--><include>**/*.*</include></includes></resource></resources><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-failsafe-plugin</artifactId><version>2.16</version><executions><execution><id>default-integration-test</id><goals><goal>integration-test</goal></goals><configuration><excludes><exclude>none</exclude></excludes><includes><include>**/*Test.java</include></includes></configuration></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.3</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>2.17</version><configuration><suiteXmlFiles><suiteXmlFile>src/main/resources/testng.xml</suiteXmlFile></suiteXmlFiles><properties><property><name>usedefaultlisteners</name><value>false</value></property><property><name>listener</name><value>org.uncommons.reportng.HTMLReporter,org.uncommons.reportng.JUnitXMLReporter</value></property></properties>      <forkMode>always</forkMode></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.2.1</version><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><transformers><transformerimplementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/spring.handlers</resource></transformer><transformerimplementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer"><resource>META-INF/spring.factories</resource></transformer><transformerimplementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/spring.schemas</resource></transformer><transformerimplementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" /><transformerimplementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"><mainClass>${start-class}</mainClass></transformer></transformers></configuration></execution></executions></plugin></plugins></build>
</project>
package org.kuohai;public class TestNGMain {
    public static void main(String[] args) {
    org.testng.TestNG.main(args);}
}
java -jar 包名.jar  -testjar 包名.jar

观众老爷们,看到这了来个一键三连不过分吧,喜欢的话记得关注哦,后期的踩坑笔记记得来围观啊 ?