CLOVER🍀

That was when it all began.

REST Assured 6.0をWildFly 39+Arquillianで試す

これは、なにをしたくて書いたもの?

2025年12月にREST Assuredが6.0.0になっていて、Eclipse Yasson 3をサポートするようになっていたので少し見ておこうかなと。

REST Assured 6.0とそれ以前

REST AssuredはREST APIをテストするためのライブラリーです。

REST Assured

Jakarta EEなどでのテストでは、よく見かけるライブラリーなのかなと思います。

REST Assured自体は便利なのですが、JSON-B 3.0を長らくサポートしていないという問題がありました。

Support JSON-B 3.0 (Jakarta EE 10) · Issue #1651 · rest-assured/rest-assured · GitHub

これがREST Assured 6.0になり解消されたというのが今回注目しているポイントです。

現在のREST Assuredは6.0と5.5の2系統がメンテナンスされているようですね。

それぞれのポイントはこちら。

  • 6.0
    • ReleaseNotes60 · rest-assured/rest-assured Wiki · GitHub
    • Java 17以上が必要
    • 内部的にGroovy 5.xを使用
    • Spring Framework 7.xをサポート、最小バージョンは5.3
    • Jackson 3をサポート
    • Eclipse Yasson 3をサポート、かつ最小バージョン
    • Apache Johnzon 2をサポート、かつ最小バージョン
  • 5.5
    • Java 8以上が必要
    • 内部的にGroovy 4.xを使用
    • Spring Framework 7.xをサポート(5.5.7以降)、最小バージョンは5.1
    • Jacksonのサポートは2まで
    • Eclipse Yasson 1をサポート
    • Apache Johnzon 1をサポート
    • Jakarta EE 10に対応

Jakarta EE 10に対応するにはREST Assured 6.0以降になるのかなと思ったのですが、どうやらそうではなさそうです。

どちらかというとJava 17以降を必要としていることが大きく、これで対応可能なライブラリーなどが変わった感じですね。
Eclipse Yassonなどが良い例です。

で、自分がJSON-B 3.0(Eclipse Yasson 3)への対応を気にしていたのは、Jakarta EEサーバーではJSONを扱う時に
JacksonではなくJSON-Bを使用していることが多く、JSON-B 3.0を使えなかったのはけっこう引っかかっていたからですね。

JSONのシリアライザーの優先順位は、Jacksonが最初のまま変わらないようですが。

  1. JSON using Jackson 3 (databind)
  2. JSON using Jackson 2 (Faster Jackson (databind))
  3. JSON using Jackson (databind)
  4. JSON using Gson
  5. JSON using Johnzon
  6. JSON-B using Eclipse Yasson
  7. XML using Jakarta EE
  8. XML using JAXB

Usage / Object Mapping / Serialization / Content-Type based Serialization

今回はWildFly 39を使って、REST Assured 6.0を簡単に試してみましょう。

環境

今回の環境はこちら。

$ java --version
openjdk 25.0.2 2026-01-20
OpenJDK Runtime Environment (build 25.0.2+10-Ubuntu-124.04)
OpenJDK 64-Bit Server VM (build 25.0.2+10-Ubuntu-124.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.9.14 (996c630dbc656c76214ce58821dcc58be960875b)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 25.0.2, vendor: Ubuntu, runtime: /usr/lib/jvm/java-25-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "6.8.0-107-generic", arch: "amd64", family: "unix"

準備

まずはテスト対象のアプリケーションを作成しましょう。

Maven依存関係など。

    <properties>
        <maven.compiler.release>25</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.wildfly.bom</groupId>
                <artifactId>wildfly-ee-with-tools</artifactId>
                <version>39.0.1.Final</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.junit</groupId>
                <artifactId>junit-bom</artifactId>
                <version>5.11.3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- Maven War Pluginにweb.xmlを省略させるため -->
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jakarta.ws.rs</groupId>
            <artifactId>jakarta.ws.rs-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jakarta.enterprise</groupId>
            <artifactId>jakarta.enterprise.cdi-api</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.arquillian.junit5</groupId>
            <artifactId>arquillian-junit5-container</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.wildfly.arquillian</groupId>
            <artifactId>wildfly-arquillian-container-remote</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.shrinkwrap.resolver</groupId>
            <artifactId>shrinkwrap-resolver-depchain</artifactId>
            <version>3.3.5</version>
            <scope>test</scope>
            <type>pom</type>
        </dependency>

        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.27.7</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>6.0.0</version>
            <scope>test</scope>
        </dependency>
        <!-- wildfly-ee BOMより-->
        <dependency>
            <groupId>org.eclipse</groupId>
            <artifactId>yasson</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>ROOT</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>3.5.5</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.wildfly.plugins</groupId>
                <artifactId>wildfly-maven-plugin</artifactId>
                <version>5.1.5.Final</version>
                <executions>
                    <execution>
                        <id>package</id>
                        <goals>
                            <goal>package</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>start-before-integration-test</id>
                        <phase>pre-integration-test</phase>
                        <goals>
                            <goal>start</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>shutdown-after-integration-test</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>shutdown</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <overwrite-provisioned-server>true</overwrite-provisioned-server>
                    <discover-provisioning-info>
                        <version>39.0.1.Final</version>
                    </discover-provisioning-info>
                </configuration>
            </plugin>
        </plugins>
    </build>

依存ライブラリーのバージョンは基本的にWildFlyのBOMで解決します。テストコードではArquillianを使いますが、この時点では
JUnit 5を使うことになります(JUnit 6は次のWildFly Arquillian Adapterでの対応です)。

Arquillianの実行形態はRemoteです。

REST Assuredは単純に追加。

GettingStarted · rest-assured/rest-assured Wiki · GitHub

Eclipse YassonはこれもWildFlyのBOMで管理されているバージョンを指定します。現時点では3.0.4です。

WildFlyはWildFly Maven Pluginでプロビジョニングします。またインテグレーションテスト時に起動と停止もできるように
しておきます。

Maven Failsafe Pluginを入れておくことをお忘れなく。

Jakarta RESTful Web Servicesの有効化。

src/main/java/org/littlewings/restassured/RestApplication.java

package org.littlewings.restassured;

import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;

@ApplicationPath("/")
public class RestApplication extends Application {
}

リソースクラス。

src/main/java/org/littlewings/restassured/echo/EchoResource.java

package org.littlewings.restassured.echo;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import java.util.Optional;

@Path("/echo")
@ApplicationScoped
public class EchoResource {
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public EchoResponse get(@QueryParam("word") String word) {
        return new EchoResponse("★★★Hello %s!!★★★".formatted(Optional.ofNullable(word).orElse("World")));
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public EchoResponse post(EchoRequest echoRequest) {
        return new EchoResponse("★★★Hello %s!!★★★".formatted(Optional.ofNullable(echoRequest.word()).orElse("World")));
    }
}

リクエスト、レスポンスクラス。

src/main/java/org/littlewings/restassured/echo/EchoRequest.java

package org.littlewings.restassured.echo;

public record EchoRequest(
        String word
) {
}

src/main/java/org/littlewings/restassured/echo/EchoResponse.java

package org.littlewings.restassured.echo;

public record EchoResponse(
        String message
) {
}

動作確認しておきましょう。WildFlyを起動。

$ mvn wildfly:dev

確認。

$ curl localhost:8080/echo
{"message":"★★★Hello World!!★★★"}


$ curl localhost:8080/echo?word=REST+Assured
{"message":"★★★Hello REST Assured!!★★★"}


$ curl -X POST localhost:8080/echo --json '{}'
{"message":"★★★Hello World!!★★★"}


$ curl -X POST localhost:8080/echo --json '{"word": "REST Assured"}'
{"message":"★★★Hello REST Assured!!★★★"}

OKですね。1度結果を削除。

$ mvn clean

REST Assuredを使ってテストコードを書く

では、REST Assuredを使ってテストコードを書きます。

src/test/java/org/littlewings/restassured/echo/EchoResourceIT.java

package org.littlewings.restassured.echo;

import io.restassured.RestAssured;
import io.restassured.config.ObjectMapperConfig;
import io.restassured.config.RestAssuredConfig;
import io.restassured.mapper.ObjectMapperType;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.io.File;
import java.net.URL;
import java.nio.file.Path;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.junit5.container.annotation.ArquillianTest;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.littlewings.restassured.RestApplication;

import static io.restassured.RestAssured.given;
import static io.restassured.RestAssured.when;
import static org.assertj.core.api.Assertions.assertThat;

@ArquillianTest
@RunAsClient
class EchoResourceIT {
    @ArquillianResource
    private URL deploymentUrl;

    private String resourcePrefix =
            RestApplication.class
                    .getAnnotation(ApplicationPath.class)
                    .value()
                    .replaceFirst("^/", "");

    @Deployment
    static WebArchive createDeployment() {
        /*
        // 依存ライブラリーが必要な場合は以下を追加
        File[] compileAndRuntimeScopeDependencyFiles =
                Maven
                        .resolver()
                        .loadPomFromFile("pom.xml")
                        .importCompileAndRuntimeDependencies()
                        .resolve()
                        .withTransitivity()
                        .asFile();
         */

        File mainResources = Path.of("src/main/resources").toFile();

        return ShrinkWrap
                .create(WebArchive.class)
                .addPackages(true, RestApplication.class.getPackage())
                .addAsResource(mainResources, "");
        // 依存ライブラリーが必要な場合は以下を追加
        // .addAsLibraries(compileAndRuntimeScopeDependencyFiles);
    }

    @BeforeEach
    void setUp() {
        RestAssured.baseURI = deploymentUrl + resourcePrefix;
        // ObjectMapperをJSON-Bで固定
        RestAssured.config = RestAssuredConfig.config().objectMapperConfig(
          ObjectMapperConfig.objectMapperConfig().defaultObjectMapperType(ObjectMapperType.JSONB)
        );
    }

    @Test
    void echoGet() {
        EchoResponse response1 =
                when()
                        .get("/echo")
                        .then()
                        .statusCode(Response.Status.OK.getStatusCode())
                        .contentType(MediaType.APPLICATION_JSON)
                        .extract()
                        .as(EchoResponse.class);

        assertThat(response1.message()).isEqualTo("★★★Hello World!!★★★");

        EchoResponse response2 =
                given()
                        .queryParam("word", "REST Assured")
                        .when()
                        .get("/echo")
                        .then()
                        .statusCode(Response.Status.OK.getStatusCode())
                        .contentType(MediaType.APPLICATION_JSON)
                        .extract()
                        .as(EchoResponse.class);

        assertThat(response2.message()).isEqualTo("★★★Hello REST Assured!!★★★");
    }

    @Test
    void echoPost() {
        EchoResponse response1 =
                given()
                        .contentType(MediaType.APPLICATION_JSON)
                        .body("{}")
                        .when()
                        .post("/echo")
                        .then()
                        .contentType(MediaType.APPLICATION_JSON)
                        .extract()
                        .as(EchoResponse.class);

        assertThat(response1.message()).isEqualTo("★★★Hello World!!★★★");

        EchoResponse response2 =
                given()
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(new EchoRequest("REST Assured"))
                        .when()
                        .post("/echo")
                        .then()
                        .contentType(MediaType.APPLICATION_JSON)
                        .extract()
                        .as(EchoResponse.class);

        assertThat(response1.message()).isEqualTo("★★★Hello World!!★★★");
    }
}

雛形的にShrinkwrap Resolversを使って依存ライブラリーもデプロイするコードを書いていたのですが、今回はcompileおよび
runtimeスコープの依存ライブラリーがないのでそのままだと実行に失敗します。

    @Deployment
    static WebArchive createDeployment() {
        /*
        // 依存ライブラリーが必要な場合は以下を追加
        File[] compileAndRuntimeScopeDependencyFiles =
                Maven
                        .resolver()
                        .loadPomFromFile("pom.xml")
                        .importCompileAndRuntimeDependencies()
                        .resolve()
                        .withTransitivity()
                        .asFile();
         */

        File mainResources = Path.of("src/main/resources").toFile();

        return ShrinkWrap
                .create(WebArchive.class)
                .addPackages(true, RestApplication.class.getPackage())
                .addAsResource(mainResources, "");
        // 依存ライブラリーが必要な場合は以下を追加
        // .addAsLibraries(compileAndRuntimeScopeDependencyFiles);
    }

依存ライブラリーを含む場合は、コメントアウトしている箇所を外しましょう。

またJSONのシリアライザーの優先順位は以下に書いてあるとおりなのですが、

Usage / Object Mapping / Serialization / Content-Type based Serialization

Shrinkwrap ResolversにGsonが含まれているのでそちらが有効になってしまいます。今回はJSON-Bを使うように明示しました。

    @BeforeEach
    void setUp() {
        RestAssured.baseURI = deploymentUrl + resourcePrefix;
        // ObjectMapperをJSON-Bで固定
        RestAssured.config = RestAssuredConfig.config().objectMapperConfig(
          ObjectMapperConfig.objectMapperConfig().defaultObjectMapperType(ObjectMapperType.JSONB)
        );
    }

あとはmvn verifyでWildFlyのプロビジョニング・起動後にテストが行われ、終了後にWildFlyが停止します。

$ mvn verify

スタックトレースを見たりして確認してみましたが、サーバー側もテスト側もJSONのシリアライズ、デシリアライズには
JSON-Bが使われています。

これでやっとサーバーとテストでリクエスト、レスポンスに関するコードの共有がまともにできるようになりました…。

オマケ

mvn verifyでWildFlyまわりの処理をしてもいいのですが、毎回行うと手間です。今回は以下のようにしました。

テストとWildFlyへのデプロイは省略して、プロビジョニングだけ行ってWildFlyを起動。バックグラウンド実行になります。

$ mvn clean package -DskipTests -Dwildfly.package.deployment.skip && mvn wildfly:start

WildFly Maven Pluginの処理やWildFlyへのデプロイはスキップしてテストだけ実行。

$ mvn verify -Dwildfly.skip -Dwildfly.package.skip

これで毎回WildFlyをプロビジョニングしたり、起動・停止する時間を省略できます。なお、-Dwildfly.skipだけでよいのではと
思うかもしれませんがこのやり方だと両方必要です。

バックグラウンドで起動したままのWildFlyを停止するにはこちら。

$ mvn wildfly:shutdown

おわりに

REST Assured 6.0をWildFly 39+Arquillianで試してみました。

以前からREST AssuredでJakarta EE 10以降のJSON-Bが使えないことが気になっていたのですが、6.0になってようやく
解決しました。

これでソースコードとテストコードで同じライブラリーを使えるので、各ライブラリーごとにシリアライズ、デシリアライズの
挙動差や設定を調整しなくてよくなりますね。