Anthony Accioly Anthony Accioly - 2 months ago 8
Java Question

Maven folder layout: Should I place tests in the EAR or its sub-modules?

We have an EAR project with several sub-modules (multiple EJBs, Web Projects, Application Clients, etc). The natural scope for unitary tests are their respective sub-modules (since they are supposed to be testing isolated units).

In a short amount of time we have introduced non obvious testing dependencies. Projects were mocking functionality from other projects, etc. Soon our architecture evolved to several stand-alone jar files with mocks (web project 1 mocks, ejb 2 mocks, etc); we wire those mocks against the EAR and consume the mocks in the sub-modules ("Skinny War" style).

EAR
Modules
WEB 1
WEB 2
EJB 2
EJB 3
etc
Libs
Shared library 1
Shared Library 2
Testing dependencies
WEB 1 mocks
WEB 2 mocks
EJB 1 mocks
EJB 2 mocks
etc

WEB1
Uses EJB 1 and EJB 3
Uses Shared Library 1
Testing
Consumes EJB 1 and EJB 2 mocks


Anyway, the consensus among our team is that mocks are getting out of control. We want to evolve towards Arquillian and testing inside the container (e.g., towards Integration tests). We are also introducing ATTD (initially just functional tests with Drone, but I wish to have a fully functional Thucydidies + JBehave or EasyB setup soon).

Tests may depend on resources from multiple sub-modules. ShrinkWrap is there to guarantee that things do not get out of hand.

So my question is: Where should I put tests, stories, Arquillian configuration files and so on?

I feel like the EAR is the best place to group everything:

EAR
Modules
Test
src
Java
Test 1
Test 2
Resources
Configuration Files
Stories
Story 1
Story 2


This would allow us to have a single unified report, forget about inter-modular dependencies, have a single point of configuration and so on.

But I might be wrong (working with per-module granularity have its advantages).

So what is considered best practice for Arquillian tests: Should I place my test files in the EAR Project? Should I place integration / acceptance tests in the EAR project and unitary tests in the sub-modules? Or should I put everything in the sub-modules?




Update: Alternative approach. Should I isolate integration tests into a separate module? If so, how (how should I set dependencies, configure Arquillian, etc)?

Answer

Let me put a little bit of practical information about how to organize integration tests with Maven so that other people struggling with it may Do The Right Thing™ .

I ended up following advice from the fantastic (even if somewhat old) Better Builds with Maven as well as Codehaus Maven and Integration Testing Guide.

  1. Use an aggregator top level project:

    myproject
        myproject-ear
        myproject-war
        myproject-ejb
        ...
        myproject-integration-tests
    
  2. As suggested by mchamati, have every module test itself with unit tests (just as before). Unit tests are fast and can be run on every build.

  3. Also for unit tests, it turns out that my initial strategy was not so bad. I still have several mock modules bound to the EAR as managed dependencies.
  4. Have a separate integration tests module, its packaging type can be pom; (you will not build an real deployable artifact out of this project; also, when the number of tests begin to grow, it may make sense to turn it into yet another another aggregator pom):

    <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/maven-v4_0_0.xsd">
        <parent>
            <artifactId>myproject</artifactId>
            <groupId>com.mycompany</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.myproject</groupId>
        <artifactId>myproject-integration-tests</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>pom</packaging>
    
  5. Following Maven conventions integration tests should go into src/it:

    <build>
        <testSourceDirectory>src/it</testSourceDirectory>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin> 
    
  6. Use failsafe to run integration tests:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-failsafe-plugin</artifactId>
        <version>2.17</version>
        <configuration>
            <encoding>${project.build.sourceEncoding}</encoding>
        </configuration>
        <dependencies>
            <dependency>
                <groupId>org.apache.maven.surefire</groupId>
                <artifactId>surefire-junit47</artifactId>
                <version>2.17</version>
            </dependency>
        </dependencies>
        <executions>
            <execution>
                <goals>
                    <goal>integration-test</goal>
                    <goal>verify</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
    
  7. Have your integration-tests project import the ear project dependencies (and everything else that you need). I'm not sure if this is considered good practice since it was not mentioned in any of the guides, but It worked very well for me.

    <dependency>
        <groupId>com.mycompany</groupId>
        <artifactId>my-project-ear</artifactId>
        <version>1.0-SNAPSHOT</version>
        <type>pom</type>
    </dependency>
    
  8. Centralize arquillian related configuration in the integration tests module:

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.jboss.arquillian</groupId>
                <artifactId>arquillian-bom</artifactId>
                <version>${arquillian.version}</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.arquillian.junit</groupId>
            <artifactId>arquillian-junit-container</artifactId>
            <version>${arquillian.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- Arquillian container related dependencies, etc -->
    
  9. Follow failsafe naming conventions for test files.

    src/it/java
        com/mycompany/myproject
            mypackage
                MyIT.java 
                MySecondIT.java
            anotherpackage
                YetAnotherIT.java  
    
  10. Profit

    Under the top level aggregator project:

    1. mvn test ⇒ Runs the fast unit tests full of mocks
    2. mvn verify ⇒ Runs the real integration / functional / acceptance tests (may be slow)

Extra hint: If you are running an CI Server such as Jenkins setup your nightly builds to run the integration tests (i.e., have your build call mvn verify). Do not deploy unverified builds to homologation or production servers.