Reputation: 2496
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
Upvotes: 11
Views: 9904
Reputation: 2496
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
Upvotes: 2
Reputation: 1818
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
Upvotes: 4
Reputation: 372
You need to install maven on the other machine to run the tests.
If maven installation is not an viable option, create a jar containing the test classes and use this jar for running the tests. check the maven documentation - http://maven.apache.org/plugins/maven-jar-plugin/examples/create-test-jar.html
Upvotes: -1