1

My Maven project foo.web has its source files in src/main and the test sources in src/test. Of course, the test classes make use of the "main" classes. Now I want to use the test classes in another project during runtime, so I followed these instructions on how to create a test-jar.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.4</version>
    <executions>
        <execution>
            <goals> 
                <goal>test-jar</goal>
            </goals>
        </execution>
    </executions>
</plugin>

This works perfectly well, a jar like web-SNAPSHOT-tests.jar is created and I can include it in my other project.

<dependency>
        <groupId>foo</groupId>
        <artifactId>web</artifactId>
        <version>SNAPSHOT</version>
        <type>test-jar</type>
</dependency>

But it seems like the dependency to web-SNAPSHOT is not correctly set, because at runtime I receive NoClassDefFoundErrors of classes which are available in foo.web. So I added another dependency:

<dependency>
        <groupId>foo</groupId>
        <artifactId>web</artifactId>
        <version>SNAPSHOT</version>
        <type>war</type>
        <scope>runtime</scope>
</dependency>

Unfortunately, this changes nothing. Does anyone know what is wrong here?

  • What are you trying to do with the tests exported as jar, run them from command line? if so, make sure to include the sources jar in the classpath when launching the JVM – Morfic Sep 18 '13 at 13:03
  • No, I want to use some helper classes defined for those tests in another project. – Stephan Windmüller Sep 18 '13 at 13:37
  • 1
    Why not extract a separate "helper/common/etc" maven module? Unless you have a good reason, I don't see why you'd want to depend on some test library that depends on another library... – Morfic Sep 18 '13 at 13:40

3 Answers3

5

WAR archives are structured differently from JARs. When running in an application server such as Tomcat or JBoss, the server will handle the WAR correctly. Since you are running outside of a server, the artifact will be used like to a normal JAR archive. Because WARs use different locations for the .class files, the NoClassDefFoundError is thrown at run time.

In a JAR, the class com.example.Foo will be stored at /com/example/Foo.class. Since WARs are designed to contain libraries, resources etc. the classes should not be stored relative to the root of the archive. Instead, they are contained in the folder /WEB-INF/classes, Foo would be stored as /WEB-INF/classes/com/example/Foo.class.

Fortunately, the Maven developers thought of this issue and added the attachClasses option to the WAR plugin. This option creates an additional JAR with the classes classifier that contains only the Java classes in JAR format (relative the the archive root).

To enable the building of this JAR, you can use this snippet in your WAR project's build section (in addition to the configuration for the maven-jar-plugin to build the test JAR):

<pluginManagement>
    <plugins>
        <!-- … -->

        <plugin>
            <!-- build the classes JAR (non-test classes) -->
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.4</version>
            <configuration>
                <attachClasses>true</attachClasses>
            </configuration>
        </plugin>
    </plugins>
</pluginManagement>

<plugins>
    <!-- … -->

    <plugin>
        <!-- build the test JAR (test classes) -->
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>2.4</version>
        <executions>
            <execution>
                <goals> 
                    <goal>test-jar</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
</plugins>

Since the classes are attached, they will be installed and deployed by Maven together with the WAR artifact. Note that this only provides you with the contents of the regular WAR archive, to use the test classes, you need to depend on both the classes artifact and the test jar. To do this, you can use:

<dependencies>
    <!-- … -->

    <dependency>
        <!-- test classes only -->
        <groupId>foo</groupId>
        <artifactId>web</artifactId>
        <version>SNAPSHOT</version>
        <type>test-jar</type>
    </dependency>

    <dependency>
        <!-- non-test classes only -->
        <groupId>foo</groupId>
        <artifactId>web</artifactId>
        <version>SNAPSHOT</version>
        <type>jar</type>
        <classifier>classes</classifier>
        <scope>runtime</scope>
    </dependency>
</dependencies>
Stewart
  • 17,616
  • 8
  • 52
  • 80
Simon Dierl
  • 111
  • 4
1

Try:

<dependency>
    <groupId>foo</groupId>
    <artifactId>web</artifactId>
    <version>SNAPSHOT</version>
    <type>war</type>
    <classifier>tests</classifier>
    <scope>test</scope>
</dependency>
user944849
  • 14,524
  • 2
  • 61
  • 83
0

Maven allows you some configuration regarding snapshots dependancy

<repository>
<id>foo-repository</id>
<url>...</url>
<snapshots>
    <enabled>true</enabled>
    <updatePolicy>XXX</updatePolicy>
</snapshots>

Check the above config. If its false in pom.xml maven will not update snapshots. Also you will find the following thread useful for your query What exactly is a Maven Snapshot and why do we need it?

Community
  • 1
  • 1
Pratik Shelar
  • 3,154
  • 7
  • 31
  • 51