Arquillianを使ったJavaEEのユニットテストをLiberty上で実施する


初めに

JavaEEを利用したプロジェクトでJUnitを利用したテストコードを用意する必要がでてきたため、eclipse上でユニットテストを実施するまでを纏めてみます。

Arquillianとは

JavaEEアプリケーションサーバのコンテナ管理と連携して、JUnit等のテスティングフレームワーク上でテストコードが実行できるため、JPAやJAX-RS等のJavaEEのコンポーネントのテストができる。
参考:http://arquillian.org

実施環境

・macOS 10.14.6
・Eclipse 2019-06
・WebSphere Application Server Version 19.0.0.7 Liberty
・Java SE 1.8

前提条件

ArquillianのGettingStartedを実施

本家ArquillianのGettingStartedを実施してみて、一通りのArquillianの動作が実施できることを確認した上で、アプリケーションサーバをLibertyに変更して実施してみる。
参考:http://arquillian.org/guides/getting_started_ja/

Liberty用のリモートコンテナを使う

上記のGettingStartedでは、WeldEE、GlassFishのエンベディッドコンテナ、JBossのマネージドコンテナを利用できるまでが網羅されているため、今回はLibertyのリモートコンテナを利用できるところまでを確認する。

Libertyのリモートコンテナのドキュメントは下記
参考:https://github.com/OpenLiberty/liberty-arquillian/blob/master/liberty-remote/README.md

テスト実施

eclipseプロジェクトの作成

前述のArquillianのGenttingStartedでは、mavenコマンドからJavaプロジェクトを作成しているが、今回はeclipseからMaven Projectを作成して専用のmavenプロジェクトを作成。
また、Mavenのプロファイルでコンテナの切り替えを実現しているが、今回は専用のプロジェクトを用意する。

サンプルコードを作成したプロジェクトにコピー

GettingStartedで利用しているGreeter.java,PhraseBuilder.javaとGreeterTest.javaを作成したプロジェクトに配置する。

pom.xmlの編集

GettingStartedで利用したpom.xmlを流用して作成する。

pom.xml
<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.arquillian.example</groupId>
    <artifactId>arquillian-liberty-remote</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.jboss.arquillian</groupId>
                <artifactId>arquillian-bom</artifactId>
                <version>1.4.0.Final</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.17</version>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.arquillian.junit</groupId>
            <artifactId>arquillian-junit-container</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.openliberty.arquillian</groupId>
            <artifactId>arquillian-liberty-remote</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun</groupId>
            <artifactId>tools</artifactId>
            <version>1.6</version>
            <scope>system</scope>
            <systemPath>/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/tools.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>8.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

tools.jarに対して依存性を追加しているのは、m2eclipseの依存解決のため。

arquillian.xmlの配置

リモートコンテナ用の設定ファイルを準備する。Arquillian Liberty Remote Documentationを参考にする。

arquillian.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<arquillian
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://jboss.org/schema/arquillian"
    xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">

    <engine>
        <property name="deploymentExportPath">target/</property>
    </engine>

    <container qualifier="liberty-remote" default="true">
        <configuration>
            <property name="hostName">localhost</property>
            <property name="serverName">defaultServer</property>
            <property name="username">admin</property>
            <property name="password">admin</property>
            <property name="httpPort">9080</property>
            <property name="httpsPort">9443</property>
        </configuration>
    </container>
</arquillian>

server.xmlへの追加

Libertyに対して設定情報を追加する。こちらもArquillian Liberty Remote Documentationを参考にする。

server.xml
<server description="new server">

    <!-- Enable features -->
    <featureManager>
        <feature>restConnector-2.0</feature>
        <feature>webProfile-8.0</feature>
    </featureManager>
    <!-- To access this server from a remote client add a host attribute to 
        the following element, e.g. host="*" -->
    <httpEndpoint httpPort="9080" httpsPort="9443"
        id="defaultHttpEndpoint" />

    <quickStartSecurity userName="admin"
        userPassword="admin" />

    <keyStore id="defaultKeyStore" password="password" />


    <!-- This section is needed to allow upload of files to the dropins directory, 
        the remote container adapter relies on this configuration -->
    <remoteFileAccess>
        <writeDir>${server.config.dir}/dropins</writeDir>
    </remoteFileAccess>

</server>

注意点としては、
- 後述のGreeterTestでCDIを利用している。 → webProfile-8.0のfeatureを利用
- restConnectorのfeatureをwebProfile-8.0に合わせて2.0を選択
上記を加味したfeatureを選択している。

証明書のインポート

この段階でLibertyを起動し、GreeterTestのshould_create_greetingのテストを実行してみる。

GreeterTest.java
    // 中略 ...
    @Test
    public void should_create_greeting() {
        Assert.assertEquals("Hello, Earthling!", greeter.createGreeting("Earthling"));
        greeter.greet(System.out, "Earthling");
    }

実行すると下記のエラーで失敗する。

org.jboss.arquillian.container.spi.client.container.LifecycleException: Could not determine remote server status : sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at io.openliberty.arquillian.remote.WLPRemoteContainer.start(WLPRemoteContainer.java:87)
    at org.jboss.arquillian.container.impl.ContainerImpl.start(ContainerImpl.java:179)
// 中略 ...

Libertyに対してSSL接続を実施しようとしているが、必要な証明書が無いため、エラーとなっている。
下記の記事を参考に証明書のインポートを行う。
参考:Java で SSL の証明書エラーを解決/回避して HTTPS で接続する方法

$ openssl s_client -connect localhost:9443 < /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > /tmp/cacert.crt
$ sudo /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/bin/keytool -import -alias localhost -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/security/cacerts -file /tmp/cacert.crt 

ファイルパスを指定している部分は適宜、環境に合わせて変更する。

JUnitの実施

Libertyが起動していることを確認の上、再度GreeterTestのshould_create_greetingのテストを実行してみる。



テストが成功し、コンソールに「Hello, Earthling!」が表示されている。

おわりに

リモートコンテナを利用したArquillianのユニットテストは、事前にアプリケーションサーバを立ち上げておくことが必要になるが、テストの実施ごとにアプリケーションサーバの起動・停止が不要であるため、そこそこのスピードでユニットテストを実施することができる。