【问题标题】:Running spring tests from executable jar从可执行 jar 运行 spring 测试
【发布时间】:2019-02-10 19:24:51
【问题描述】:

我有一些 Spring 测试可以启动应用程序上下文并测试一些服务。我能够使用 Maven 和通过 IDE 运行这些测试。现在我需要在无法访问 Maven 的另一台机器上运行这些测试。我的想法是创建一个测试 jar 并通过命令行运行它们。

所以我创建了一个自定义 Runner,它调用我需要的测试类,这些测试将启动 Spring 应用程序上下文并测试一些服务。

这里是示例代码:

我的自定义跑步者:

public class Main {

    public static void main(String[] args) {
        System.out.println("Running tests!");
        JUnitCore engine = new JUnitCore();
        engine.addListener(new TextListener(System.out));
        engine.run(SpringSampleTest.class);
    }
} 

上面的运行器正在调用这个测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {AppConfig.class})
public class SpringSampleTest {
    @Autowired
    TestService testService;

    @Test
    public void testSimple() {
        assertTrue("Test Simple", testService.isValid());
    }
}

这是我的配置和服务

@Configuration
@ComponentScan(basePackages = {"mypackage"})
public class AppConfig {

}

@Service
public class TestService {

    public boolean isValid() {
        return true;
    }
}

所以为了从 jar 中运行这些测试,我使用了 assembly-plugin 创建了一个可执行 jar,其中包含我所有的测试和依赖项(感谢这个答案 here)。现在,当我运行这个可执行 jar 时,我的自定义 runner(Main.java) 能够触发测试,但它没有加载 Spring Context 并且由于我的依赖项没有自动装配而失败并出现 NullPointer 异常。这是日志:

Running tests!
Sep 05, 2018 5:15:01 PM org.springframework.test.context.support.DefaultTestContextBootstrapper getDefaultTestExecutionListenerClassNames
INFO: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: []
Sep 05, 2018 5:15:01 PM org.springframework.test.context.support.DefaultTestContextBootstrapper getTestExecutionListeners
INFO: Using TestExecutionListeners: []
.E
Time: 0.007
There was 1 failure:
1) testSimple(com.c0deattack.cu.runners.SpringSampleTest)
java.lang.NullPointerException
    at com.c0deattack.cu.runners.SpringSampleTest.testSimple(SpringSampleTest.java:19)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.junit.runners.Suite.runChild(Suite.java:128)
    at org.junit.runners.Suite.runChild(Suite.java:27)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:105)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:94)
    at com.c0deattack.cu.runners.Main.main(Main.java:15)

FAILURES!!!
Tests run: 1,  Failures: 1

有人可以指出我做错了什么吗?

我还添加了我的 pom.xml 和程序集描述符文件:

pom.xml

<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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>mypackage</groupId>
    <artifactId>executable-tests</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>

    <name>executable-tests</name>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.3.2.RELEASE</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>

            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptor>src/main/assembly/assembly.xml</descriptor>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>assembly</goal>
                        </goals>
                        <configuration>
                            <archive>
                                <manifest>
                                    <mainClass>mypackage.Main</mainClass>
                                </manifest>
                            </archive>
                        </configuration>
                    </execution>
                </executions>
            </plugin>            
        </plugins>
    </build>
</project>

描述符 - assembly.xml

<assembly
        xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
    <id>fat-tests</id>
    <formats>
        <format>jar</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <dependencySets>
        <dependencySet>
            <outputDirectory>/</outputDirectory>
            <useProjectArtifact>true</useProjectArtifact>
            <unpack>true</unpack>
            <scope>test</scope>
        </dependencySet>
    </dependencySets>
    <fileSets>
        <fileSet>
            <directory>${project.build.directory}/test-classes</directory>
            <outputDirectory>/</outputDirectory>
            <includes>
                <include>**/*.class</include>
            </includes>
            <useDefaultExcludes>true</useDefaultExcludes>
        </fileSet>
    </fileSets>
</assembly>

您可以在 github 中查看示例项目:https://github.com/SaiUpadhyayula/executabletests

【问题讨论】:

  • 集成/单元测试应该与 maven 一起抛出,并且创建的工件(jar)不应包含任何测试代码。为什么需要在 maven 阶段之外测试代码?
  • @CRISTIANROMEROMATESANZ 我在问题中添加了更多详细信息,tl.dr:我想在我无法访问 Maven 的另一台机器上运行测试。因此,我想创建一个 jar 并从中执行测试。
  • 你也应该在主函数中创建spring context
  • 你有一个困难的要求。我看到两个选项,要么编写不使用 spring 上下文的测试(通过重载或基于环境选择的构建器),要么模拟你的 spring bean。
  • 干得好赛!您在 Github 中的项目运行良好!

标签: java spring maven


【解决方案1】:

我刚刚分叉了您的存储库,但发现了错误。 Spring 工厂必须与 Maven 抛出测试时使用的相同。必须像测试上下文一样委托给 Spring Ic。

查看拉取请求以获取更多信息:

https://github.com/SaiUpadhyayula/executabletests/pull/2

【讨论】:

  • 当我从 JAR 运行时,testService 仍然为空。你能做一个干净的包并运行jar - executable-tests-1.0-fat-tests.jar 吗?
  • 今晚我会试一试。一旦我确认我会写解决方案。
  • 我刚刚更新了代码,运行 fat jar 运行良好。见图片答案附件
【解决方案2】:

我在调试测试后终于发现了问题,原来Spring在引导TestContext时会在spring-test jar中的META-INF/spring.factories文件中查找TestExecutionListener。

这个 spring.factories 文件最好放在我的可执行 jar 的 META-INF 文件夹中。但是组装插件所做的是,它没有添加正确的 spring.factories 文件,其中包含必要的 TestExecutionListener

将此文件添加到 src/main/resources/META-INF/spring.factories 解决了问题

# Default TestExecutionListeners for the Spring TestContext Framework
#
org.springframework.test.context.TestExecutionListener = \
    org.springframework.test.context.web.ServletTestExecutionListener,\
    org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener,\
    org.springframework.test.context.support.DependencyInjectionTestExecutionListener,\
    org.springframework.test.context.support.DirtiesContextTestExecutionListener,\
    org.springframework.test.context.transaction.TransactionalTestExecutionListener,\
    org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener

# Default ContextCustomizerFactory implementations for the Spring TestContext Framework
#
org.springframework.test.context.ContextCustomizerFactory = \
    org.springframework.test.context.web.socket.MockServerContainerContextCustomizerFactory

【讨论】:

  • 您的回复与我的同时回复:)。查看我的拉取请求以及显示测试结果的附加配置。
【解决方案3】:

你需要在另一台机器上安装 maven 来运行测试。

如果 maven 安装不是一个可行的选项,请创建一个包含测试类的 jar 并使用此 jar 来运行测试。检查 Maven 文档 - http://maven.apache.org/plugins/maven-jar-plugin/examples/create-test-jar.html

【讨论】:

  • 我不能在另一台机器上使用 Maven,这就是我创建 Jar 的原因。
  • 更新了评论。检查创建测试 jar 是否适合您。
猜你喜欢
  • 2021-08-17
  • 2013-05-04
  • 1970-01-01
  • 2020-10-15
  • 2014-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-26
相关资源
最近更新 更多