Hello all,

I'm running up against an issue that I'm sure has come up countless times.
How can we share test dependencies in a principled way? I would like to
configure our projects such that:

   1. For a project *P*, its tests are associated with the project, so that
   `mvn install` from the project directory *p/* fails when P's tests fail.
   2. Some of P's testing functionality (e.g., stubs, testing utility
   classes, JUnit superclasses) are packaged for use in the tests of other
   projects* D* that depend on P.
   3. P's shared testing functionality has dependencies on P. For example,
   P defines a repository interface R, and its tests define and use an RStub
   that implements R. We want to package RStub for use by D's tests.
   4. When D declares a scope:test dependency on P's testing functionality,
   automatically pull in the transitive dependencies of that testing
   functionality.
   5. Avoid cycles in the Maven dependency graph.

Some things I've tried so far:

   - Create test-jars for P. This fails on #2 and #4:
   - The test-jars include all of P's test classes instead of just the
      testing functionality that needs to be shared for use by D's tests.
      - All transitive dependencies of P's test-jar must be manually
      specified with scope:test in the D's POM.
   - Extract the stubs to an independent "P - Stubs" project, which P's
   tests depend on.
      - p-stubs depends on P.
      - Stubs, testing utilities, etc. go in p-stubs/src/main/.
      - P depends on p-stubs with scope:test.
      - D depends on p-stubs with scope:test.
      - This fails on #5: it causes a cycle in the dependency graph since P
      depends on p-stubs (scope:test) and p-stubs depends on P. See
      https://stackoverflow.com/questions/10174542
   - Create an independent "P - Tests" project, with P's stubs and tests.
      - p-tests depends on P.
      - Stubs, testing utilities, etc. go in p-tests/src/main/.
      - P's tests go in p-tests/src/test/.
      - D depends on p-tests with scope:test.
      - This almost works but fails on #1: P's build succeeds when its
      tests fail.
      - This approach also seems to be fighting Maven and IDE tooling. For
      example, NetBeans will automatically create p/src/test even
though we never
      intend to put anything there.

My searches so far have not turned up any complete solutions to this
problem. The maven-jar-plugin documentation has a section
<https://maven.apache.org/plugins/maven-jar-plugin/examples/create-test-jar.html#The_preferred_way>
that touches on this issue:

The preferred way

In order to let Maven resolve all test-scoped transitive dependencies you
should create a separate project.


   1. <project>
   2.    <groupId>groupId</groupId>
   3.     <artifactId>artifactId-tests</artifactId>
   4.     <version>version</version>
   5.   ...
   6. </project>


   - Move the sources files from src/test/java you want to share from the
   original project to the src/main/java of this project. The same type of
   movement counts for the resources as well of course.
   - Move the required test-scoped dependencies and from the original
   project to this project and remove the scope (i.e. changing it to the
   compile-scope). And yes, that means that the junit dependency (or any
   other testing framework dependency) gets the default scope too. You'll
   probably need to add some project specific dependencies as well to let it
   all compile again.

Now you have your reusable *test-classes* and you can refer to it as you're
used to...

This is along the lines of the "P - Stubs" approach I suggested above, but
it unfortunately cannot work since the stubs themselves depend on P (fails
on #3 above).

Is there a satisfying way to solve this problem? It seems to me like any
one of the following changes would resolve the issue.

   - Allow P to depend on a separate project p-stubs with scope:test, even
   though p-stubs depends on P (instead of calling it a dependency cycle).
   This means that the build order would be a bit awkward: P (compile) →
   p-stubs (compile) → P (test).
   - Allow P to indicate that its tests are in another project p-tests.
   That way, `mvn install` in p/ would continue to p-tests/ and then fail P's
   build if the tests in p-tests fail.
   - Introduce a new "stubs" concept to the project model, adding a new
   p/stubs directory for project P. A stubs-compile step would occur between
   compile and test-compile. The stubs and tests of D could depend on P's
   stubs (perhaps automatically by virtue of D's dependency on P).

Maybe one of these—or a better alternative—is already possible? I feel like
I must be missing something. Is something wrong with the way I'm
structuring my projects? Does Maven already provide a way to achieve this
out-of-the-box? Is there a plugin that provides something like the "stubs"
functionality?

Thanks!
Brandon

p.s. Sorry that my first message to the list is so long!

Reply via email to