Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running spring tests from executable jar

Tags:

java

spring

maven

I have some Spring tests which spin up the application context and tests some services. I am able to run these tests using Maven and through IDE. Now I have a requirement to run these tests on the other machine where there is no access to Maven. My idea was to create a test jar and run them through the command line.

So I created a custom Runner which invokes the test classes I need and these Tests will spin up a Spring Application context and tests some service.

Here is the sample code:

My Custom Runner:

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);
    }
} 

The above runner is invoking this test

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

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

Here is my config and the Service

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

}

@Service
public class TestService {

    public boolean isValid() {
        return true;
    }
}

So to run these tests from a jar, I have used assembly-plugin to create an executable jar which includes all my tests and dependencies(Thanks to this answer here ). Now, when I am running this executable jar, My custom runner(Main.java) is able to trigger the test, but it is not loading the Spring Context and is failing with a NullPointer exception because my dependencies are not autowired. Here is the log:

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

Can somebody please point out what I am doing wrong?

I am also adding my pom.xml and assembly descriptor files:

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>

descriptor - 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>

You can have a look at the sample project in github : https://github.com/SaiUpadhyayula/executabletests

like image 700
Sai Upadhyayula Avatar asked Sep 05 '18 15:09

Sai Upadhyayula


People also ask

How do I run a test case from a jar file?

Go To Export. Select Jar or Runnable Jar as per your use click on next. Specify the main file name. Click on Finish.

How do you run a spring boot executable jar?

The Spring Boot Maven Plugin implement a custom ClassLoader to locate and load all the external jar libraries now nested inside the package. automatically find the main() method and configure it in the manifest, so we don't have to specify the main class in our java -jar command.

Can I run spring boot executable jar from commandline?

In one word, we call it a methodology where an entire Spring Boot application is built into a single executable Jar archive and includes all the dependencies, packaged as nested Jars. The plugins in Maven does all the dirty work and provides you with the FAT Jar, which can be run easily using the command of java -jar.


2 Answers

I have just forked your repository and I found the error. Spring factory must be the same than the one is used when test are thrown by maven. It is a must to delegate into Spring Ic in the same way like test context.

See pull request for aditional information:

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

enter image description here

enter image description here

enter image description here

like image 186
CRISTIAN ROMERO MATESANZ Avatar answered Oct 20 '22 00:10

CRISTIAN ROMERO MATESANZ


I finally found the problem after debugging the test, it turns out that Spring while bootstrapping the TestContext will look for the TestExecutionListener's defined inside the META-INF/spring.factories file inside the spring-test jar.

This spring.factories file should be ideally placed inside the META-INF folder of my executable jar. But what the assembly-plugin does is, it was not adding the right spring.factories file which contains the necessary TestExecutionListener's

adding this file to src/main/resources/META-INF/spring.factories solved the problem

# 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
like image 43
Sai Upadhyayula Avatar answered Oct 19 '22 22:10

Sai Upadhyayula