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を作成したプロジェクトに配置する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457284/d90472e0-a258-68d5-193a-e0458b833153.png)

##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](https://github.com/OpenLiberty/liberty-arquillian/blob/master/liberty-remote/README.md)を参考にする。


```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](https://github.com/OpenLiberty/liberty-arquillian/blob/master/liberty-remote/README.md)を参考にする。

```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 で接続する方法](https://qiita.com/KenjiOtsuka/items/0bf38365b8df6e08af18)

```
$ 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のテストを実行してみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457284/31184fd8-d963-5bcd-bbb6-970d2363354f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457284/de283658-ca16-4377-034a-a8c109e073ec.png)
テストが成功し、コンソールに「Hello, Earthling!」が表示されている。

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