45

I have two projects, foo and foo-web under the com.example group. foo-web depends on foo.

To be able to develop the UI part of the application without depending on external services, dummy DAOs were implemented in foo (they return static data so we don't have to connect to databases etc).

We were required to move the dummy classes to src/test/java. This means that they don't get deployed with foo.jar to the war built from the web project. I found these instructions on the maven site, but they don't seem to work for me.

In foo's pom.xml I have:

        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <executions>
            <execution>
              <id>test-jar</id>
              <phase>test-compile</phase>
              <goals>
                <goal>test-jar</goal>
              </goals>
            </execution>
          </executions>
        </plugin>

When running mvn install on foo-web, in the target of foo I'd get two jars: foo-1.0.0-SNAPSHOT.jar and foo-1.0.0-SNAPSHOT-tests.jar. They both get installed fine in the local maven repository.

Before, the foo-web dependency looked like this:

<dependency>
    <groupId>com.example</groupId>
    <artifactId>foo</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

And that would trigger the deployment of foo-1.0.0-SNAPSHOT.jar in the war. Now, I want to also deploy the -tests jar, preferably only for a "local" profile.

I tried in various ways to do this:

<profile>
    <id>local</id>
    <dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>foo</artifactId>
            <version>1.0.0-SNAPSHOT</version>
            <type>test-jar</type>
        </dependency>
    </dependencies>
</profile>

This causes the source jar to be deployed with a different name: com.example-foo.jar and does not deploy the test jar. I also tried using <classifier> instead of <type> in the dependency, but it still does the same. I tried using the above dependency outside of the profile (alongside the others), but it still behaves the same.

If I add the <type> to the main dependency (without adding the other dependency) I get the test jar deployed (with the same name as above), but the source, naturally, does not get deployed.

The only difference from what's written in the documentation is the fact that the scope is not specified for the test dependency. Does it only work for the test scope? Can I somehow deploy the test classes differently.

I know the question's a bit convoluted, please let me know if there's something I can clarify.

Thanks!


Update:

I tried it in several more ways but it still won't work.

I added another execution to the maven-jar-plugin in the foo project (the dependency, not the main web project) in which I hoped to force maven to compile the test classes in the same jar as the main ones and reference the big bundle by a different classifier. I couldn't get it to work:

<execution>
  <id>local-build</id>
  <phase>package</phase>
  <goals>
    <goal>jar</goal>
  </goals>
  <configuration>
    <classifier>batman</classifier>
    <directory>${basedir}/src/test/java</directory> <!-- tried several variations here -->
    <includes>
        <include>**</include>
    </includes>
  </configuration>
</execution>

The jar was generated with the batman classifier, but I couldn't find any way to get it to include test classes in the jar goal.

Doing this, I realized that this does not depend on the test-jar type/tests classifier/test scope relationship. When I tried to specify the new jar I'm building besides the main one, I got the same behavior as when trying to include the -tests jar. I checked the local maven repository and both all jars from the dependent project are getting installed fine, so the problem is the main project's dependency resolution.

tl;dr

Everything boils down to the question if you can include the same dependency with multiple classifiers. From what I saw until now, the answer is no - I always get the com.example-foo jar when specifying the same dependency multiple times with different classifiers.

Alex Ciminian
  • 11,398
  • 15
  • 60
  • 94
  • 2
    Maybe the following answer will help you - it helped me in similar problem: http://stackoverflow.com/a/1733745 – loomchild May 16 '12 at 06:54

5 Answers5

56

Better to configure maven pom file in your first module

<project>
    <groupId>com.example</groupId>
    <artifactId>foo</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <build>
        <plugins>
            ...
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>test-jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

After this a mvn install/release will also deploy an artifact foo-1.0.0-SNAPSHOT-tests.jar

Then configure the dependency on the test jar with classifier (like suggested in other responses)

<dependency>
    <groupId>com.example</groupId>
    <artifactId>foo</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <type>test-jar</type>
    <!-- uncomment if needed in test scope only
         <scope>test</scope>
    -->
</dependency>
ᄂ ᄀ
  • 5,669
  • 6
  • 43
  • 57
raisercostin
  • 8,777
  • 5
  • 67
  • 76
  • 2
    I think it is clearer to use `tests` than `test-jar`. – Jesse Glick Jun 01 '16 at 09:08
  • 5
    The documentation here https://maven.apache.org/pom.html says "The type often corresponds to the packaging used, though this is also not always the case. Some examples are jar, ejb-client and test-jar." They might want to reserve the classifier "to distinguish artifacts that were built from the same POM but differ in their content" like for jdk version. But in practice might work. – raisercostin Jun 01 '16 at 23:53
  • is strongly preferred. is technical (package format), is purpose. – toolforger Nov 29 '22 at 08:58
4

I see you use test-jar as type and you used classifier instead of type but probably also with test-jar ... but did you try the following?

<dependency>
    <groupId>com.example</groupId>
    <artifactId>foo</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <classifier>tests</classifier>
    <scope>test</scope>
</dependency>
Brambo
  • 835
  • 7
  • 12
  • Yep, I did - `tests`, but without the `` property - I need the test jar at runtime. – Alex Ciminian Oct 27 '11 at 15:55
  • 1
    @AlexCiminian: "I need the test jar at runtime." That is a sentence that I would definitely give a second and third thought. If you really need it at runtime, it might indicate that you have productive code inside your tests instead in the right place!? (If I missed some very special situation, I have to excuse myself for not reading the initial two and a half screen pages describing your whole problem. :) ) – Tom Fink Mar 06 '17 at 09:38
  • @TomFink I had the same issue and my situation is the following: I have a project (let's call it CorrectnessTests), which checks that another project (FooTests) implements all its tests correctly. I.e. I define in CorrectnessTests that every test in FooTests should follow certain rules, that there should be at least n tests of a specific kind etc. This necessitates exactly the case of needing a test-jar of FooTests at runtime of CorrectnessTests. – 2v0mjdrl Jun 27 '17 at 11:56
  • 2
    @ThePadawan But if the functionality of the "CorrectnessTests" module is doinf testing, why not implement it as a test (or more) there. Then you again end up in a test-test relation. :) But I understand the problem. We just recently had a situation where a tool that acts as "stimulator" for a message driven system was using test code of another module. So this resulted in the same situation. I solved it by moving some utility code to the productive code. Because otherwise the created jar-with-dependencies would have contained all the test frameworks dependencies... :( – Tom Fink Aug 02 '17 at 13:34
2

I'm a bit late to the party, but I hope this helps someone: you can include multiple types of the same dependency. Assume your project depends on common-artifact-1.0.jar, and there's also a test jar common-artifact-1.0-tests.jar.

You can import both the jar and the tests jar by doing this:

<dependencies>
    <dependency>
        <groupId>my.corp</groupId>
        <artifactId>common-artifact</artifactId>
        <version>1.0</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>my.copr</groupId>
        <artifactId>common-artifact</artifactId>
        <version>1.0</version>
        <type>test-jar</type>
        <scope>test</scope> <!-- the "compile" scope also works here -->
    </dependency>
</dependencies>
Jeronimo Backes
  • 6,141
  • 2
  • 25
  • 29
1

One working solution that I found was to use the build-helper plugin to get the test folder in the assembly I'm building for the local profile. This is in the dependent project:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <!-- used to package the dummy daos when building 
         with the local profile -->
    <artifactId>build-helper-maven-plugin</artifactId>
    <executions>
        <execution>
            <phase>generate-sources</phase>
            <goals>
                <goal>add-source</goal>
            </goals>
            <configuration>
                <sources>
                    <source>${extra.sources.dir}</source>
                </sources>
            </configuration>
        </execution>
    </executions>
</plugin>

And the profile, also in the dependent project:

<profile>
    <id>local</id>
    <properties>
        <env.resources.dir>src/test/resources</env.resources.dir>
        <extra.sources.dir>src/test/java</extra.sources.dir>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.8.1</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>org.springframework.test</artifactId>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</profile>

This way, the main project's pom does not get modified and the stubs get deployed when building locally.

I still haven't found out if you can deploy multiple classifiers, though. :)

Alex Ciminian
  • 11,398
  • 15
  • 60
  • 94
  • Unfortunately, this wreaks havoc on IntelliJ, which expects a single source root to only exist in a single module. – Jeff Evans Jun 05 '19 at 19:09
0

If you need the jars at runtime you might also want to try the following trick. This just copies the dependencies directly to your target folder when compiling. I expect that they will be included when a package is made ... I haven't tested this myself

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <executions>
    <execution>             
      <id>copy-dependencies</id>
      <phase>compile</phase>
      <goals>
        <goal>copy-dependencies</goal>
      </goals>
      <configuration>
        <artifactSet>
          <includes>
        <include>com.example:foo</include>
          </includes>
        </artifactSet>
        <outputDirectory>target/dependencies</outputDirectory>
      </configuration>
    </execution>
  </executions>
</plugin> 
Brambo
  • 835
  • 7
  • 12