Add Maven dependencies to your Arquillian micro-deployments

Arquillian is a testing framework which lets you write real integration tests, run inside the container of your choice. With Arquillian you will be writing micro-deployments for your tests, small Java artifacts that is, which contains the bare minimum of classes and resoruces needed for your test to be executed within your container. To build these artifacts you will be using the ShrinkWrap API. You don’t have to adopt the micro-deployment strategy of course, but do you really want your whole project classpath available for the test of e.g. a single EJB component? Micro-deployments will isolate your test scenario and deploy to the container much faster than if you would bring in the entire classpath of your project.

When writing integration tests with Arquillian and ShrinkWrap you will probably, sooner or later, run into a use case where your test depend on a third party library. Since it would be very inconvenient to declare all the necessary classes in the third party library by hand, ShrinkWrap provides a way to attach complete libraries to your micro-deployment. If you’re using Maven, the Maven dependency resolver feature is very convenient. The 1.0.0.Final version of Arquillian is using a 1.0 beta version of the ShrinkWrap resolver module. The resolver API has in my opinion improved a lot for the latest 2.0 version (currently in Alpha) and I really recommend anyone using the resolver API to use the 2.0 version instead.

If you want to use the latest resovler API, you have to add it to the dependencyManagement tag in your Maven pom.xml before the actual Arquillian BOM, to make sure the 2.0 version of the resolver module will be loaded first:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.jboss.shrinkwrap.resolver</groupId>
            <artifactId>shrinkwrap-resolver-bom</artifactId>
            <version>2.0.0-alpha-1</version>
            <scope>test</scope>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.jboss.arquillian</groupId>
            <artifactId>arquillian-bom</artifactId>
            <version>1.0.0.Final</version>
            <scope>test</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</dependencyManagement>

To add third party libraries from your Maven dependencies to your micro-deployment, use the ShrinkWrap resolver API to add the necessary libaries to your artifact. In the example below, the commons-io and json libraries are specified with their respective Maven coordinates:

@Deployment
public static Archive createDeployment() {
    return ShrinkWrap.create(WebArchive.class, "fileviewer.war")
            .addClass(EnableRest.class)
            .addClass(FileViewer.class)
            .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
            .addAsLibraries(
                DependencyResolvers.use(MavenDependencyResolver.class)
                    .artifact("commons-io:commons-io:2.1")
                    .artifact("org.json:json:20090211")
                    .resolveAsFiles());
}

As you can see, it’s very easy to add libraries from your Maven dependencies to your ShrinkWrap micro-deployments. The behaviour of the ShrinkWrap resolver API can also be customized far more than what was shown in the above example. It is worth noting that in the example, any dependencies of the specified artifacts will be included as well. Transitivity is however one aspect which can be customized further through the API. An example project which uses the ShrinkWrap Maven dependency resolver can be found on GitHub.

 

Tommy Tynjä
@tommysdk

8 thoughts on “Add Maven dependencies to your Arquillian micro-deployments”

  1. Thanks Tommy!

    It’s worth nothing that any dependencies of the artifacts specified will be included as well. In this case, that doesn’t apply since the two libraries in the example don’t have any dependencies. If you don’t want transitive dependencies to be resolved, there’s a method to disable that behavior per artifact or globally.

  2. Thanks for your feedback Dan. I’ve now clarified the transitive dependencies aspect in the blog post.

  3. Hello Everyone, I am a newbie to shrinkwrap.
    In my project i am using Arquillian as my integration testing framework and I am trying to resolve a blocking issue.
    I am trying to make a war file using the following code

    MavenDependencyResolver resolver = DependencyResolvers.use(
    MavenDependencyResolver.class).loadMetadataFromPom(“pom.xml”)
    .artifacts(“org.jboss.spec:jboss-javaee:6.0.1.0.0.Final”,…);

    File[] ExtLibs =resolver.resolveAsFiles();
    return ShrinkWrap.create(WebArchive.class, “test.war”). addClasses(LoginModule.class,Realm.class,IAccount.class).addAsLibraries(ExtLibs).addAsManifestResource(EmptyAsset.INSTANCE, “beans.xml”);

    When i run this test case i am getting the following error

    java.lang.RuntimeException: Could not invoke deployment method: public static org.jboss.shrinkwrap.api.spec.WebArchive com.auth.ControllerLoginModuleTest.createDeployment()
    at org.jboss.arquillian.container.test.impl.client.deployment.AnnotationDeploymentScenarioGenerator.invoke(AnnotationDeploymentScenarioGenerator.java:160)

    I don’t understand what i am doing wrong. Any light on the above issue is greatly appreciated.
    Thanks

  4. Hi Shashank. Without seeing the actual stacktrace, the error message “java.lang.RuntimeException: Could not invoke deployment method” when working with MavenDependencyResolver will most likely occur when a specified artifact cannot be resolved from a local or remote repository. You specify that you want to get hold of the artifact “org.jboss.spec:jboss-javaee:6.0.1.0.0.Final”, but it should be on the following format: “org.jboss.spec:jboss-javaee-6.0:1.0.0.Final”. You’re not specifying what container you’re using, but you should most likely not need to include this dependency as it should be provided by your Java EE 6 container at runtime. You could therefore try to just omit that dependency from your micro-deployment.

  5. Thanks for quick reply Tommy,
    I tried removing it. I am still getting the same error.
    So, my project had dependency on many other projects. I made jars of each project and tried to insert into maven repository and then through dependency resolver I am trying to access the jars.
    I have one question here. If the path in the maven repository is say

    ~/.m2/repository/junit/junit/4.11/junit-4.11.jar

    I am including “junit:junit:4.11″ in Dependency resolver as artifact. I wanted some help from you in confirming wether this method is correct or I am missing something.

    I am using Glassfish -remote-3.1 as my Application server.
    Below is the full stack trace that I am getting after i removed the dependency for javaee.

    java.lang.RuntimeException: Could not invoke deployment method: public static org.jboss.shrinkwrap.api.spec.WebArchive com.appdynamics.auth.ControllerLoginModuleTest.createDeployment()
    at org.jboss.arquillian.container.test.impl.client.deployment.AnnotationDeploymentScenarioGenerator.invoke(AnnotationDeploymentScenarioGenerator.java:160)
    at org.jboss.arquillian.container.test.impl.client.deployment.AnnotationDeploymentScenarioGenerator.generateDeployment(AnnotationDeploymentScenarioGenerator.java:94)
    at org.jboss.arquillian.container.test.impl.client.deployment.AnnotationDeploymentScenarioGenerator.generate(AnnotationDeploymentScenarioGenerator.java:57)
    at org.jboss.arquillian.container.test.impl.client.deployment.DeploymentGenerator.generateDeployment(DeploymentGenerator.java:79)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94)
    at org.jboss.arquillian.core.impl.EventContextImpl.invokeObservers(EventContextImpl.java:99)
    at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:81)
    at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:135)
    at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:115)
    at org.jboss.arquillian.core.impl.EventImpl.fire(EventImpl.java:67)
    at org.jboss.arquillian.container.test.impl.client.ContainerEventController.execute(ContainerEventController.java:100)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94)
    at org.jboss.arquillian.core.impl.EventContextImpl.invokeObservers(EventContextImpl.java:99)
    at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:81)
    at org.jboss.arquillian.test.impl.TestContextHandler.createClassContext(TestContextHandler.java:75)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94)
    at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:88)
    at org.jboss.arquillian.test.impl.TestContextHandler.createSuiteContext(TestContextHandler.java:60)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94)
    at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:88)
    at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:135)
    at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:115)
    at org.jboss.arquillian.test.impl.EventTestRunnerAdaptor.beforeClass(EventTestRunnerAdaptor.java:80)
    at org.jboss.arquillian.junit.Arquillian$2.evaluate(Arquillian.java:182)
    at org.jboss.arquillian.junit.Arquillian.multiExecute(Arquillian.java:314)
    at org.jboss.arquillian.junit.Arquillian.access$100(Arquillian.java:46)
    at org.jboss.arquillian.junit.Arquillian$3.evaluate(Arquillian.java:199)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.jboss.arquillian.junit.Arquillian.run(Arquillian.java:147)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
    Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.jboss.arquillian.container.test.impl.client.deployment.AnnotationDeploymentScenarioGenerator.invoke(AnnotationDeploymentScenarioGenerator.java:156)
    … 50 more
    Caused by: java.lang.RuntimeException: Could not create new descriptor instance
    at org.jboss.shrinkwrap.resolver.api.DependencyBuilderInstantiator.createFromUserView(DependencyBuilderInstantiator.java:101)
    at org.jboss.shrinkwrap.resolver.api.DependencyResolvers.use(DependencyResolvers.java:39)
    at com.appdynamics.auth.ControllerLoginModuleTest.createDeployment(ControllerLoginModuleTest.java:67)
    … 55 more
    Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at org.jboss.shrinkwrap.resolver.api.DependencyBuilderInstantiator.createFromUserView(DependencyBuilderInstantiator.java:96)
    … 57 more
    Caused by: java.lang.NoSuchMethodError: org.codehaus.plexus.DefaultPlexusContainer.lookup(Ljava/lang/Class;)Ljava/lang/Object;
    at org.jboss.shrinkwrap.resolver.impl.maven.MavenRepositorySystem.getRepositorySystem(MavenRepositorySystem.java:220)
    at org.jboss.shrinkwrap.resolver.impl.maven.MavenRepositorySystem.(MavenRepositorySystem.java:64)
    at org.jboss.shrinkwrap.resolver.impl.maven.MavenBuilderImpl.(MavenBuilderImpl.java:105)
    … 62 more

    Thanks in advance for any help further.

  6. Shashank: I’m glad if I can help. First of all, you don’t need to package JUnit along with your deployment. Arquillian manages that for you by enriching each deployment with the needed classes from your test framework. This error is however indicating that some of the required dependencies for the ShrinkWrap Maven Resolver are not available on your test classpath. Make sure you have the proper dependencies available in your pom.xml. I also recommend you to post a question about this issue on the ShrinkWrap user forum: https://community.jboss.org/en/shrinkwrap?view=discussions. Hopefully this can help you navigate towards the root cause of your problem.

  7. Thanks Tommy, I have posted the question on jboss communty.
    In my pom I have the following entries

    org.jboss.shrinkwrap.resolver
    shrinkwrap-resolver-bom
    ${version.shrinkwrap.resolver}
    import
    pom

    org.jboss.arquillian
    arquillian-bom
    ${version.arquillian_core}
    pom
    import

    com.controllerjars
    controllerapi
    2.0.0

    com.controllerjars
    controllerauth
    1.0.0

    com.controllerjars
    controllerbeans
    1.0.0

    And i am using maven Resolver to resolve these dependencies.
    File[] files = Resolvers.use(MavenResolverSystem.class).loadPomFromFile(“pom.xml”).resolve(“com.controllerjars:controllerbeans:1.0.0″,”com.controllerjars:controllerapi:2.0.0″,”com.controllerjars:controllerauth:1.0.0″)
    .withTransitivity().as(File.class);

    return ShrinkWrap.create(WebArchive.class, “test.war”).
    addClasses(ControllerLoginModule.class,AuthRealm.class,IAccountManagerInternal.class).addAsLibraries(files).addAsManifestResource(EmptyAsset.INSTANCE, “beans.xml”);

    Thanks for any help further.

  8. Shashank: I think the cause of this problem is due to ambiguous ShrinkWrap Resolver dependencies. You should declare the ShrinkWrap Resolver Bill of Materials in the dependency Management chain before the Arquillian BOM. I recommend using the ShrinkWrap Resolver 2.0.0-beta-2. In the dependency section you need to have JUnit, shrinkwrap-resolver-depchain and arquillian-junit-container dependencies present. The ShrinkWrap Resolver 2.0.0-beta-2 provides a new API for resolving artifacts, so you need to make sure you use that particular API. I threw together a quick example project with the bare minimum dependencies needed to make it work, and a test case using an embedded TomEE container. You can have a look at it here: https://github.com/tommysdk/showcase/tree/master/arquillian-shrinkres

Comments are closed.