Tuesday, 11 July 2017

Testing Java EE 7 Applications in Docker with Arquillian-Cube

Arquillian is a powerful and flexible testing framework that allows you to test your applications or frameworks with your target application server. The tests that you write can run standalone (client mode) or inside the container (in-container mode). This capability allows you to run your tests in an environment that is closer to your production than is possible using unit tests and mocks alone.

Arquillian-Cube is an extension that provides Arquillian with the ability to manage Docker containers. This allows you to even more closely approximate your production environment. Arquillian will create and start the necessary Docker containers and deploy test artifacts prior to your tests executing. In addition to a Docker container running a Java EE application server, you may specify any other Docker container that may be needed to support your application such as a database or load balancer.

Oracle Java EE, Oracle Java Applications, Oracle Java Certifications
Figure 1. An example Arquillian based test deployment with an Apache web tier, WebLogic, and the Oracle Database being run in Docker containers.

The Sample Application


The project presented here builds upon Steve Button's weblogic-with-arquillian sample. I will discuss the steps that were necessary to migrate from a test suite running against a local WebLogic installation to a test suite running against a Dockerized WebLogic instance. The code for this sample is available in the weblogic-with-arquillian-cube project on GitHub.

Adding the Maven Dependencies

Importing the bill of material (BOM) dependencies in the dependencyManagement section of the pom ensures that consistent versions of the Arquillian and Arquillian-Cube and all transitive dependencies are used:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.arquillian</groupId>
            <artifactId>arquillian-universe</artifactId>
            <version>${version.arquillian_universe}</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</dependencyManagement>

A dependency for arquillian-cube-docker needs to be added along with arquillian-junit since the tests are written for JUnit using the Arquillian test-runner:

<dependency>
    <groupId>org.arquillian.universe</groupId>
    <artifactId>arquillian-cube-docker</artifactId>
    <scope>test</scope>
    <type>pom</type>
</dependency>

<dependency>
    <groupId>org.arquillian.universe</groupId>
    <artifactId>arquillian-junit</artifactId>
    <scope>test</scope>
    <type>pom</type>
</dependency>

Finally, a weblogic profile is added to the project along with a dependency to arquillian-wls-remote-rest.

Using Maven profiles allows you to define several containers and switch between them using a command-line switch. This allows you to test your application against different application servers or environments very easily.

The WebLogic Remote Rest container will upload and deploy/undeploy test artifacts using REST to the remote WebLogic server. 

<profiles>
     <profile>
         <id>weblogic</id>
         <activation>
             <activeByDefault>true</activeByDefault>
         </activation>
         <properties>
            <arquillian.launch>weblogic</arquillian.launch>
         </properties>
         <dependencies>
             <dependency>
                <groupId>org.jboss.arquillian.container</groupId>
                <artifactId>arquillian-wls-remote-rest</artifactId>
                <version>${version.arquillian-wls-remote-rest}</version>
                <scope>test</scope>
             </dependency>
         </dependencies>
     </profile>
</profiles>


Configuring Arquillian


arquillian.xml contains two extensions and a container definition. The first is the cube extension, which allows you to specify configuration options for Arquillian-Cube. Here I am only specifying one such option, the connectionMode. In this case, this setting is configuring Arquillian-Cube to use an existing container if one is already running, and to leave the container running after the test completes. This is a useful feature if you're running tests on your local machine; it's faster on subsequent invocations and you can access the running container to read logs or even attach a debugger.

<extension qualifier="cube">
    <property name="connectionMode">STARTORCONNECTANDLEAVE</property>
</extension>

The docker extension is used to configure options specific to Docker. dockerContainersFile is a reference to a docker-compose compatible file. The docker-compose tool is not used to create the services, rather the file is parsed and services are created using the docker-java library.

serverUri specifies the method by which Arquillian-Cube will communicate with Docker. If you do not specify this property it is inferred based on your platform. Setting this explicitly on the Mac overrode the default behavior of using boot2docker and worked with docker-engine directly.

<extension qualifier="docker">
    <property name="autoStartContainers">true</property>
    <property name="dockerContainersFile">docker-compose.yml</property>
    <property name="serverVersion">1.12</property>
    <property name="serverUri">unix:///var/run/docker.sock</property>
    <property name="tlsVerify">false</property>
</extension>

Finally, the weblogic container definition which is used by the WebLogic Remote Arquillian Container specifies the configuration needed to connect to and deploy applications to the remote server. This container definition is the same as if WebLogic were running directly on the host:

<container qualifier="weblogic" default="true">
    <configuration>
        <property name="adminUrl">http://localhost:7001</property>
        <property name="adminUserName">weblogic</property>
        <property name="adminPassword">welcome1</property>
        <property name="target">AdminServer</property>
    </configuration>
</container>

Docker configuration


The last change needed is the addition of the docker-compose.yml file referenced by arquillian.xml. I used an image built from the Example of Image with WLS Domain sample. If you needed to you could take advantage of Docker's extension mechanism to add configuration (such as a JMS Server or Datasource) as in the 1221-domain-with-resources sample image.

weblogic:
  image: 1221-domain:latest
  ports:
      - "7001:7001"
  environment:
      - PRODUCTION_MODE=dev
Running the Test Applications

You can run the test application from an IDE or from the command-line by invoking Maven:

$ mvn test
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building wlsarq 1.0
[INFO] ------------------------------------------------------------------------

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running buttso.demo.weblogic.wlsarq.IndexPageTest
CubeDockerConfiguration:
  serverVersion = 1.12
  serverUri = unix:///var/run/docker.sock
  tlsVerify = false
  dockerServerIp = localhost
  definitionFormat = COMPOSE
  autoStartContainers =
  clean = false
  removeVolumes = true
  dockerContainers = containers:
  weblogic:
    alwaysPull: false
    env: !!set {PRODUCTION_MODE=dev: null}
    image: 1221-domain:latest
    killContainer: false
    manual: false
    portBindings: !!set {7001/tcp: null}
    readonlyRootfs: false
    removeVolumes: true
networks: {}

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 10.19 sec - in buttso.demo.weblogic.wlsarq.IndexPageTest
Running buttso.demo.weblogic.wlsarq.PingPongBeanTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 7.36 sec - in buttso.demo.weblogic.wlsarq.PingPongBeanTest

Results :

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 20.222 s
[INFO] Finished at: 2017-04-12T17:04:52-04:00
[INFO] Final Memory: 20M/437M
[INFO] ------------------------------------------------------------------------