Version 12

    A fairly common requirement is to add JAR files (libraries) to the test archive. The bulk of the content in this answer predated the existence of ShrinkWrap Resolvers, a library for resolving artifacts from a repository (e.g., a Maven repository) and returning them as ShrinkWrap archives.

     

    The libraries are referenced by GAV, which stands for groupId, artifactId and optional version. Each segment is separated by a colon. The versions can be read in from the project's pom.xml. The resolveAsFiles() method resolves not only the artifact specified, but its transitive dependencies as well.

     

    Here's an example of resolving the guava library for inclusion in a web archive (war):

     

    @Deployment
    public static Archive<?> createTestArchive()
    {
       MavenDependencyResolver resolver = DependencyResolvers
         .use(MavenDependencyResolver.class)
         .loadMetadataFromPom("pom.xml");
    
       WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")
             .addClasses(...)
             .addAsLibraries(resolver.artifact("com.google.guava:guava:11.0.2").resolveAsFiles())
             .addAsWebResource(EmptyAsset.INSTANCE, "beans.xml");
       // verify that the JAR files ended up in the WAR
       System.out.println(war.toString(true));
       return war;
    }
    

     

    To use the ShrinkWrap Resolvers library, you'll need to add the dependency to your project:

     

    <dependency>
        <groupId>org.jboss.shrinkwrap.resolver</groupId>
        <artifactId>shrinkwrap-resolver-impl-maven</artifactId>
        <scope>test</scope>
    </dependency>
    

     

    The version is set by the Arquillian BOM in the dependency management section.

     

    The remainder of the FAQ talks about how this problem was solved prior to using ShrinkWrap Resolvers (or if you want to do it other ways). The instructions are specific to a Maven project, but the concept can be extended for other types of projects as well (Gradle, etc).

     

    Caching remote dependencies

     

    If your project is using Maven, the testCompile step will resolve any test dependencies and put them in your local Maven repository. By the time the test executes, you can be sure that the JAR files you need will be in your local repository. That allows us to simply reference a file in a central place on the local filesystem to include it into the test archive.

     

    Resolving cached dependencies

     

    Here's a fairly crude resolver that converts a Maven artifact reference into a java.io.File object (also supports a single argument shorthand notation that can resolve one or more artifacts).

     

    public class MavenArtifactResolver
    {
       private static final String LOCAL_MAVEN_REPO =
             System.getProperty("maven.repo.local") != null ?
                   System.getProperty("maven.repo.local") :
                   (System.getProperty("user.home") + File.separatorChar +
                   ".m2" + File.separatorChar + "repository");
    
       public static File resolve(final String groupId, final String artifactId,
          final String version)
       {
          return resolve(groupId, artifactId, version, null);
       }
    
       public static File resolve(final String groupId, final String artifactId,
          final String version, final String classifier)
       {
          return new File(LOCAL_MAVEN_REPO + File.separatorChar +
                groupId.replace(".", File.separator) + File.separatorChar +
                artifactId + File.separatorChar +
                version + File.separatorChar +
                artifactId + "-" + version +
                (classifier != null ? ("-" + classifier) : "") + ".jar");
       }
    
       public static File resolve(final String qualifiedArtifactId)
       {
          String[] segments = qualifiedArtifactId.split(":");
          if (segments.length == 3)
          {
             return resolve(segments[0], segments[1], segments[2]);
          }
          else if (segments.length == 4)
          {
             return resolve(segments[0], segments[1], segments[2], segments[3]);
          }
          throw new IllegalArgumentException("Invalid qualified artifactId syntax: " +
             qualifiedArtifactId);
       }
    
       public static File[] resolve(final String... qualifiedArtifactIds)
       {
          int n = qualifiedArtifactIds.length;
          File[] artifacts = new File[n];
          for (int i = 0; i < n; i++)
          {
             artifacts[i] = resolve(qualifiedArtifactIds[i]);
          }
    
          return artifacts;
       }
    }

     

    If you are using an alternative location for your local Maven repository, controlled using the maven.repo.local Maven property, you will need to promote that property as a Java system property in the surefire plugin configuration:

     

    <plugin>       
       <artifactId>maven-surefire-plugin</artifactId>
       <version>2.4.3</version>
       <configuration>
          <systemProperties>
             <property>
                <name>maven.repo.local</name>
                <value>${maven.repo.local}</value>
             </property>
          </systemProperties>
       </configuration>
    </plugin>
    

     

     

    Otherwise, the default location will be used.

     

    Including dependency artifacts

     

    You'll now add the required JAR files into your archive in the Arquillian @Deployment method. Let's assume that we need to add commons-logging and commons-lang.

     

    When you need to add a JAR file to your artifact, you should use an artifact that is a library container, such as a WAR or an EAR (though you could also use Archive#merge() to dump the classes into a JavaArchive). In this case, we'll choose a WAR. Notice that I've added the beans.xml to the WEB-INF directory, which is required if I want to use CDI in a WAR deployment.

     

     

    @Deployment
    public static Archive<?> createTestArchive()
    {
       WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")
             .addClasses(...)
             .addAsLibraries(MavenArtifactResolver.resolve(
                "commons-logging:commons-logging:1.1.1",
                "commons-lang:commons-lang:2.5",
                "org.testng:testng:5.10:jdk15"
             ))
             .addAsWebResource(EmptyAsset.INSTANCE, "beans.xml");
       // verify that the JAR files ended up in the WAR
       System.out.println(war.toString(true));
       return war;
    }
    

     

    Using staged artifacts

     

    If you want to be able to leverage the metadata in your POM, such as the versions defined in the dependencyManagement section or the dependencies associated with a particular scope, you can use the Maven dependency plugin to copy the artifacts into a directory under target during the process-test-resources phase. Then you can reference those artifacts from your test case.

     

    Here's the plugin configuration to that hand picks JAR files that you'll need in the tests:

     

     

    <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-dependency-plugin</artifactId>
       <version>2.1</version>
       <executions>
          <execution>
             <id>copy-test-libs</id>
             <phase>process-test-resources</phase>
             <configuration>
                <artifactItems>
                   <artifactItem>
                      <groupId>commons-lang</groupId>
                      <artifactId>commons-lang</artifactId>
                      <!-- version defined in the dependencyManagement section -->
                      <overwrite>false</overwrite>
                   </artifactItem>
                </artifactItems>
                <outputDirectory>
                   ${project.build.directory}/test-libs
                </outputDirectory>
                <stripVersion>true</stripVersion>
             </configuration>
             <goals>
                <goal>copy</goal>
             </goals>
          </execution>
       </executions>
    </plugin>
    

     

     

    You can also have it copy all the artifacts bound to a particular scope:

     

    <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-dependency-plugin</artifactId>
       <version>2.1</version>
       <executions>
    
          <execution>
             <id>copy-test-libs</id>
             <phase>process-test-resources</phase>
             <configuration>
                <includeScope>test</includeScope>
                <outputDirectory>
                   ${project.build.directory}/test-libs
                </outputDirectory>
                <stripVersion>true</stripVersion>
             </configuration>
             <goals>
                <goal>copy-dependencies</goal>
             </goals>
          </execution>
    
       </executions>
    </plugin>
    

     

    Now you can reference the JARs you've prepared into the target/test-libs directory in your test case:

     

     

    @Deployment
    public static Archive<?> createTestArchive()
    {
       WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")
             .addClasses(...)
             .addAsLibraries(new File("target/test-libs/commons-lang.jar"))
             .addAsWebResource(EmptyAsset.INSTANCE, "beans.xml");
       // verify that the JAR files ended up in the WAR
       System.out.println(war.toString(true));
       return war;
    }
    

     

     

    Note: This approach assumes that the current working directory is the root of the project, which may not be the case in certain execution environments.

     

    Looking ahead

     

    We're also working on a native way to resolve dependencies in ShrinkWrap to bypass the whole build configuration. See ARQ-66. This planned functionality may be used either via the ShrinkWrap API or declared using annotations on the @Deployment method. We welcome your ideas.