JAX-RS+CDIをArquillianでテスト

2014-09-08T00:00:00+00:00 Java JAX-RS

公式ドキュメント: http://arquillian.org/guides/getting_started_ja/

参考: Arquillianでインテグレーションテスト最初の第一歩

タイトル通り、JAX-RS+CDIなのをArquillianでテストしてみようっていうネタ

構成

├── build.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── sample
    │   │       ├── Greeter.java
    │   │       ├── Hoge.java
    │   │       ├── SampleController.java
    │   │       └── SampleApplication.java
    │   └── webapp
    │       └── WEB-INF
    │           ├── beans.xml
    │           └── web.xml
    └── test
        ├── java
        │   └── sample
        │       └── SampleTest.java
        └── resources
            └── arquillian.xml

てな感じで。まずアプリケーション側を先に

SampleApplication.java

package sample;

import javax.ws.rs.ApplicationPath;

import org.glassfish.jersey.server.ResourceConfig;

@ApplicationPath("resources")
public class SampleApplication extends ResourceConfig {
    public SampleApplication() {
        packages("sample");
    }
}

Hoge.java

package sample;

public class Hoge implements Greeter {
    @Override
    public String greet() {
        return "hoge";
    }
}

※Greeterはただのインターフェースなので省略する

SampleController.java

package sample;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

@Path("/sample")
public class SampleController {

    @Inject
    Greeter greeter;

    @Path("/greet")
    @GET
    public String greet() {
        return greeter.greet();
    }
}

src/main/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">
</web-app>

JAX-RS準拠なglassfishを使うので特に設定する必要なく、SampleApplicationで@ApplicationPathで設定しているので、/resources/sample/greetでアクションを実行出来るっていう感じで

src/main/WEB-INF/beans.xml

<?xml version="1.0" ?>
<beans
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
    version="1.1"
    bean-discovery-mode="all">

</beans>

これがないと@Injectによる依存性の注入が上手く作用しないはずなので

これでアプリケーション側はオッケー。まぁ試しにglassfishとかにデプロイしてみりゃ実行出来るかと思うので

でこれをarquillianを使ってテストする

build.gradle

apply plugin: "java"
apply plugin: "war"
apply plugin: "eclipse"

repositories {
    mavenCentral()
}

dependencies {
    compile "javax:javaee-api:7.0"

    testCompile "junit:junit:+"
    testCompile "org.hamcrest:hamcrest-all:+"
    testCompile "org.jboss.arquillian.junit:arquillian-junit-container:+"

    providedCompile "org.glassfish.jersey.containers:jersey-container-servlet:2.+"

    // arquillianでテストする際のContainer Adapterが必要になる。今回はglassfishを使ってるのでそれに関係するのを利用する。Container Adapter等は https://docs.jboss.org/author/display/ARQ/Container+adapters に載ってる
    providedRuntime "org.jboss.arquillian.container:arquillian-glassfish-embedded-3.1:+"
    providedRuntime "org.glassfish.main.extras:glassfish-embedded-all:4.0+"
}

※適当なので使ってるやつのバージョン指定は適当にしてますが(ry

src/test/resouces/arquillian.xml

Container Adapterな設定が必要になるので

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

    <container qualifier="glassfish-embedded" default="true">
    </container>
</arquillian>

あとはテストを書くだけ

SampleTest.java

package sample;

import java.io.File;
import java.net.URL;

import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.ShrinkWrap;
//import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;

@RunWith(Arquillian.class)
public class SampleTest {

    @ArquillianResource
    URL deploymentUrl;

    @Deployment
    public static WebArchive createDeployment() {
        return ShrinkWrap.create(WebArchive.class)
            .addPackage("sample")
            // .addClasses(Greeter.class, Hoge.class, SampleApplication.class, SampleController.class)

            .addAsWebInfResource(new File("src/main/webapp/WEB-INF/beans.xml"))
            // .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")

            .addAsWebInfResource(new File("src/main/webapp/WEB-INF/web.xml"));
    }

    @RunAsClient
    @Test
    public void test_greeter() {
        String response = target(deploymentUrl.toString() + "/resources/sample/greet")
            .request()
            .get(String.class);

        assertThat(response, is("hoge"));
    }

    private WebTarget target(String uri) {
        return ClientBuilder.newClient().target(uri);
    }
}

Arquillianでテストする際には動的にデプロイを作るけど、そのマウントURLが動的(あくまでコンテキストルートが)になるので@ArquillianResourceを使ってURLを取ればそのURLを取得する事が可能

あと@RunAsClientは参考サイトに書いてあるけど、@RunWith(Arquillian.class)をしているテストでは@Testがサーバー上で動くとの事、@RunAsClientはクライアント上で動作するとの事らしい

てな感じでJAX-RS+CDIな構成なテストをJerseyTestじゃなくてArquillianを使って利用する事も出来る。あとremoteなパッケージとかもあるのでそこら辺はまた後日やる予定

arquillian-remote ActiveRecord Validation #5 - Common Validation Options/Strict Validation -