これは、なにをしたくて書いたもの?
こちらのエントリーの続きです。
Jakarta EE 11で追加されたJakarta Data 1.0を試す - CLOVER🍀
一緒にArquillianを使ったテストコードも書いていたのですが、エントリーがあまりに長くなったので分割することにしました。
Jakarta DataとArquillian
今回、ArquillianはWildFly Arquillian AdaptorのRemoteで使うことにします。
それくらいで困ることはないかなと思っていましたが、リポジトリーインターフェースに対するテストを書こうとすると
意外とハマりました。
Jakarta Persistence(JPA)を使う場合も同じかもしれませんが。
リポジトリーインターフェースを使ってdeleteなどを実行するテストを書こうとすると、こんな感じで
「アクティブなトランザクションがない」と言われてテストに失敗します。
IdentifiedTestException [collectedExceptions={[engine:junit-jupiter]/[class:org.littlewings.jakarta.data.book.BookRepositoryIT]/[method:basicRepository()]=jakarta.data.exceptions.DataException: No active transaction for update or delete query}] at deployment.6141daa7-f824-4a3d-ae38-924a7eb0cd3c.war//org.jboss.arquillian.junit5.container.JUnitJupiterTestRunner$ArquillianTestMethodExecutionListener.getTestResult(JUnitJupiterTestRunner.java:113) at deployment.6141daa7-f824-4a3d-ae38-924a7eb0cd3c.war//org.jboss.arquillian.junit5.container.JUnitJupiterTestRunner$ArquillianTestMethodExecutionListener.access$100(JUnitJupiterTestRunner.java:72) at deployment.6141daa7-f824-4a3d-ae38-924a7eb0cd3c.war//org.jboss.arquillian.junit5.container.JUnitJupiterTestRunner.execute(JUnitJupiterTestRunner.java:61) at deployment.6141daa7-f824-4a3d-ae38-924a7eb0cd3c.war//org.jboss.arquillian.protocol.servlet5.runner.ServletTestRunner.executeTest(ServletTestRunner.java:139) at deployment.6141daa7-f824-4a3d-ae38-924a7eb0cd3c.war//org.jboss.arquillian.protocol.servlet5.runner.ServletTestRunner.execute(ServletTestRunner.java:117) at deployment.6141daa7-f824-4a3d-ae38-924a7eb0cd3c.war//org.jboss.arquillian.protocol.servlet5.runner.ServletTestRunner.doGet(ServletTestRunner.java:86) at jakarta.servlet.api@6.1.0//jakarta.servlet.http.HttpServlet.service(HttpServlet.java:633) at jakarta.servlet.api@6.1.0//jakarta.servlet.http.HttpServlet.service(HttpServlet.java:723) at io.undertow.servlet@2.0.0.Final//io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74) at io.undertow.servlet@2.0.0.Final//io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62) at io.undertow.servlet@2.0.0.Final//io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68) at io.undertow.servlet@2.0.0.Final//io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
Jakarta Dataというより、JPAを使う時にArquillianとどう組み合わせようかという問題な気がしないでもないですが。
テストコードに@Transactionalを付けても効かないようですし、Arquillian Transaction Extensionもだいぶ更新されて
いないようなので困ったのですが。
TCKはどうなっているのだろうと見てみたらUserTransactionをそのまま使っていたので、もうこれでいいかという気に
なりました。
ポイントはこれくらいですね。
環境
今回の環境はこちら。
$ java --version openjdk 25.0.3 2026-04-21 OpenJDK Runtime Environment (build 25.0.3+9-2-24.04.2-Ubuntu) OpenJDK 64-Bit Server VM (build 25.0.3+9-2-24.04.2-Ubuntu, mixed mode, sharing) $ mvn --version Apache Maven 3.9.16 (2bdd9fddda4b155ebf8000e807eb73fd829a51d5) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 25.0.3, 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-124-generic", arch: "amd64", family: "unix"
データベースはMySQLで、IPアドレスは172.17.0.2で動作しているものとします。
MySQL localhost:33060+ ssl SQL > select version(); +-----------+ | version() | +-----------+ | 8.4.10 | +-----------+ 1 row in set (0.0008 sec)
準備
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>40.0.1.Final</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.junit</groupId> <artifactId>junit-bom</artifactId> <version>5.14.3</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <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>jakarta.transaction</groupId> <artifactId>jakarta.transaction-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>jakarta.persistence</groupId> <artifactId>jakarta.persistence-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>jakarta.data</groupId> <artifactId>jakarta.data-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.hibernate.orm</groupId> <artifactId>hibernate-core</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <scope>test</scope> </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> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.eclipse</groupId> <artifactId>yasson</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> </dependencies> <build> <finalName>ROOT</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.15.0</version> <configuration> <annotationProcessorPaths> <path> <groupId>org.hibernate.orm</groupId> <artifactId>hibernate-processor</artifactId> </path> </annotationProcessorPaths> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <version>3.5.6</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>6.0.0.Final</version> <executions> <execution> <id>package</id> <goals> <goal>package</goal> </goals> </execution> </executions> <configuration> <overwrite-provisioned-server>true</overwrite-provisioned-server> <discover-provisioning-info> <version>40.0.1.Final</version> <add-ons> <add-on>mysql</add-on> </add-ons> <suggest>true</suggest> </discover-provisioning-info> <env> <MYSQL_DATASOURCE>MySqlDs</MYSQL_DATASOURCE> <MYSQL_JNDI>java:jboss/datasources/MySqlDs</MYSQL_JNDI> <MYSQL_URL>jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&connectionCollation=utf8mb4_0900_bin</MYSQL_URL> <MYSQL_USER>kazuhira</MYSQL_USER> <MYSQL_PASSWORD>password</MYSQL_PASSWORD> <MYSQL_ENABLED>true</MYSQL_ENABLED> </env> </configuration> </plugin> </plugins> </build>
前のエントリーから、テストに関する依存関係やプラグインを追加しています。主にはArquillianとREST Assuredですね。
ソースコードも前のエントリーと同じですが、載せておきましょう。
JPAのエンティティークラス。
src/main/java/org/littlewings/jakarta/data/book/Book.java
package org.littlewings.jakarta.data.book; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Table; @Entity @Table(name = "book") public class Book { @Id private String isbn; private String title; private Integer price; public static Book create(String isbn, String title, Integer price) { var book = new Book(); book.setIsbn(isbn); book.setTitle(title); book.setPrice(price); return book; } // getter/setterは省略 }
persistence.xml。
src/main/resources/META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="https://jakarta.ee/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_2.xsd" version="3.2"> <persistence-unit name="MyPersistenceUnit" transaction-type="JTA"> <jta-data-source>java:jboss/datasources/MySqlDs</jta-data-source> <properties> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="true"/> <property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/> </properties> </persistence-unit> </persistence>
Jakarta Dataを使ったリポジトリーインターフェース。
src/main/java/org/littlewings/jakarta/data/book/BookRepository.java
package org.littlewings.jakarta.data.book; import jakarta.data.Order; import jakarta.data.Sort; import jakarta.data.repository.By; import jakarta.data.repository.CrudRepository; import jakarta.data.repository.Find; import jakarta.data.repository.Param; import jakarta.data.repository.Query; import jakarta.data.repository.Repository; import java.util.List; @Repository public interface BookRepository extends CrudRepository<Book, String> { @Find Book findByIsbn(String isbn); @Find List<Book> findAll(Order<Book> order); @Find Book findByTitle(String title); @Find Book findByTitle2(@By("title") String name); @Query("select b from Book as b where b.price > :greaterThan and b.price < :lessThan") List<Book> findByPriceRange(int greaterThan, @Param("lessThan") int p2, Sort<Book> sort); }
Jakarta RESTful Web Servicesのリソースクラス。
src/main/java/org/littlewings/jakarta/data/book/BookResource.java
package org.littlewings.jakarta.data.book; import jakarta.data.Order; import jakarta.data.Sort; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.PUT; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import java.util.List; @Path("/books") @ApplicationScoped @Transactional public class BookResource { @Inject private BookRepository bookRepository; @GET @Path("/{isbn}") @Produces(MediaType.APPLICATION_JSON) public Book findByIsbn(@PathParam("isbn") String isbn) { return bookRepository.findById(isbn).orElse(null); } @GET @Produces(MediaType.APPLICATION_JSON) public List<Book> findAll() { return bookRepository.findAll(Order.by(Sort.desc(Book_.PRICE))); } @GET @Path("/query") @Produces(MediaType.APPLICATION_JSON) public List<Book> findByPrice( @QueryParam("greaterThanPrice") int greaterThanPrice, @QueryParam("lessThanPrice") int lessThanPrice) { return bookRepository.findByPriceRange(greaterThanPrice, lessThanPrice, Sort.asc(Book_.PRICE)); } @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Book register(Book book) { return bookRepository.insert(book); } @PUT @Path("/{isbn}") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public void update(@PathParam("isbn") String isbn, Book book) { bookRepository.findById(isbn).ifPresent(b -> bookRepository.save(book)); } @DELETE @Path("/{isbn}") public void delete(@PathParam("isbn") String isbn) { bookRepository.deleteById(isbn); } @DELETE public void deleteAll() { bookRepository.deleteAll(bookRepository.findAll().toList()); } @Inject private EntityManager entityManager; @POST @Path("/rollback") @Consumes(MediaType.APPLICATION_JSON) public void rollback(List<Book> books) { bookRepository.save(books.getFirst()); entityManager.flush(); bookRepository.save(books.get(1)); throw new RuntimeException("Oops!!"); } }
JAX-RSの有効化。
src/main/java/org/littlewings/jakarta/data/RestApplication.java
package org.littlewings.jakarta.data; import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Application; @ApplicationPath("/") public class RestApplication extends Application {}
テストコードを作成する
それでは、Arquillianを使ったテストコードを作成します。インテーグレーションテストとして書きます。
Arquillianの設定。特になにか設定をしているわけではないので、作成しなくてもよいのですが。
src/test/resources/arquillian.xml
<?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="remote" default="true"> </container> </arquillian>
リポジトリーインターフェースのテストコード
src/test/java/org/littlewings/jakarta/data/book/BookRepositoryIT.java
package org.littlewings.jakarta.data.book; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import jakarta.data.Order; import jakarta.data.Sort; import jakarta.data.exceptions.EntityExistsException; import jakarta.data.exceptions.OptimisticLockingFailureException; import jakarta.inject.Inject; import jakarta.persistence.EntityManager; import jakarta.transaction.UserTransaction; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.List; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit5.container.annotation.ArquillianTest; import org.jboss.modules.maven.ArtifactCoordinates; import org.jboss.modules.maven.MavenResolver; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.jupiter.api.Test; import org.littlewings.jakarta.data.RestApplication; @ArquillianTest class BookRepositoryIT { @Inject private UserTransaction userTransaction; @Inject private EntityManager entityManager; @Inject private BookRepository bookRepository; @Deployment static WebArchive createDeployment() throws IOException { File mainResources = Path.of("src/main/resources").toFile(); return ShrinkWrap.create(WebArchive.class) .addPackages(true, RestApplication.class.getPackage()) .addAsResource(mainResources, "") .addAsLibraries(MavenResolver.createDefaultResolver() .resolveJarArtifact(ArtifactCoordinates.fromString("org.assertj:assertj-core:3.27.7"))); } private void withTransaction(Runnable runner) { try { userTransaction.begin(); entityManager.createNativeQuery("truncate table book").executeUpdate(); userTransaction.commit(); userTransaction.begin(); runner.run(); userTransaction.commit(); } catch (Exception e) { throw new RuntimeException(e); } } @Test void basicRepository() { withTransaction(() -> { var books = List.of( Book.create("978-4774183169", "パーフェクト Java EE", 3520), Book.create("978-4297156589", "作って学ぶ Spring Boot入門", 2970)); // insert bookRepository.saveAll(books); assertThat(bookRepository.findAll().count()).isEqualTo(2); assertThat(bookRepository.findById("978-4774183169").get().getTitle()) .isEqualTo("パーフェクト Java EE"); assertThat(bookRepository.findById("978-4297156589").get().getPrice()) .isEqualTo(2970); // insert bookRepository.save(Book.create("978-4297136130", "プロになるためのSpring入門 -- ゼロからの開発力養成講座", 3960)); assertThat(bookRepository.findAll().count()).isEqualTo(3); // update bookRepository.save(Book.create("978-4297156589", "作って学ぶ Spring Boot入門", 3970)); assertThat(bookRepository.findById("978-4297156589").get().getPrice()) .isEqualTo(3970); // delete bookRepository.deleteById("978-4297136130"); assertThat(bookRepository.findAll().count()).isEqualTo(2); // deleteAll bookRepository.deleteAll(books); assertThat(bookRepository.findAll().count()).isZero(); }); } @Test void crudRepository() { withTransaction(() -> { // 存在しないレコードにupdateしても失敗する assertThatThrownBy(() -> bookRepository.update(Book.create("978-4774183169", "パーフェクト Java EE", 3520))) .isExactlyInstanceOf(OptimisticLockingFailureException.class) .hasMessage( "Unexpected row count (expected row count 1 but was 0) [update book set price=?,title=? where isbn=?] for entity [org.littlewings.jakarta.data.book.Book with id '978-4774183169']"); // insert bookRepository.insert(Book.create("978-4774183169", "パーフェクト Java EE", 3520)); assertThat(bookRepository.findAll().count()).isEqualTo(1); assertThat(bookRepository.findById("978-4774183169").get().getTitle()) .isEqualTo("パーフェクト Java EE"); // プライマリーキーが重複するレコードをinsertすると失敗する assertThatThrownBy(() -> bookRepository.insert(Book.create("978-4774183169", "パーフェクト Java EE", 3520))) .isExactlyInstanceOf(EntityExistsException.class) .hasMessage( "could not execute statement [Duplicate entry '978-4774183169' for key 'book.PRIMARY'] [insert into book (price,title,isbn) values (?,?,?)]"); // update bookRepository.update(Book.create("978-4774183169", "パーフェクト Java EE", 4520)); assertThat(bookRepository.findAll().count()).isEqualTo(1); assertThat(bookRepository.findById("978-4774183169").get().getPrice()) .isEqualTo(4520); }); } @Test void repositoryMethods() { withTransaction(() -> { var books = List.of( Book.create("978-4774183169", "パーフェクト Java EE", 3520), Book.create("978-4297156589", "作って学ぶ Spring Boot入門", 2970), Book.create("978-4297136130", "プロになるためのSpring入門 -- ゼロからの開発力養成講座", 3960)); bookRepository.insertAll(books); // @Find assertThat(bookRepository.findByIsbn("978-4774183169").getTitle()).isEqualTo("パーフェクト Java EE"); // @Find+Order var sortedBooks = bookRepository.findAll(Order.by(Sort.desc(Book_.PRICE))); assertThat(sortedBooks.getFirst().getTitle()).isEqualTo("プロになるためのSpring入門 -- ゼロからの開発力養成講座"); assertThat(sortedBooks.getFirst().getPrice()).isEqualTo(3960); assertThat(sortedBooks.get(1).getTitle()).isEqualTo("パーフェクト Java EE"); assertThat(sortedBooks.get(1).getPrice()).isEqualTo(3520); assertThat(sortedBooks.getLast().getTitle()).isEqualTo("作って学ぶ Spring Boot入門"); assertThat(sortedBooks.getLast().getPrice()).isEqualTo(2970); // @Find assertThat(bookRepository.findByTitle("パーフェクト Java EE").getPrice()).isEqualTo(3520); // @Find+@By assertThat(bookRepository.findByTitle2("作って学ぶ Spring Boot入門").getPrice()) .isEqualTo(2970); // @Query+@Param+Sort var priceRangeBooks = bookRepository.findByPriceRange(3500, 4000, Sort.asc(Book_.PRICE)); assertThat(priceRangeBooks).hasSize(2); assertThat(priceRangeBooks.getFirst().getTitle()).isEqualTo("パーフェクト Java EE"); assertThat(priceRangeBooks.get(1).getTitle()).isEqualTo("プロになるためのSpring入門 -- ゼロからの開発力養成講座"); }); } }
今回のポイントは、テストをUserTransactionを使ったトランザクション内で実行していることです。
private void withTransaction(Runnable runner) { try { userTransaction.begin(); entityManager.createNativeQuery("truncate table book").executeUpdate(); userTransaction.commit(); userTransaction.begin(); runner.run(); userTransaction.commit(); } catch (Exception e) { throw new RuntimeException(e); } } @Test void basicRepository() { withTransaction(() -> { var books = List.of( Book.create("978-4774183169", "パーフェクト Java EE", 3520), Book.create("978-4297156589", "作って学ぶ Spring Boot入門", 2970)); // insert bookRepository.saveAll(books); assertThat(bookRepository.findAll().count()).isEqualTo(2); assertThat(bookRepository.findById("978-4774183169").get().getTitle()) .isEqualTo("パーフェクト Java EE"); assertThat(bookRepository.findById("978-4297156589").get().getPrice()) .isEqualTo(2970); // insert bookRepository.save(Book.create("978-4297136130", "プロになるためのSpring入門 -- ゼロからの開発力養成講座", 3960)); assertThat(bookRepository.findAll().count()).isEqualTo(3); // update bookRepository.save(Book.create("978-4297156589", "作って学ぶ Spring Boot入門", 3970)); assertThat(bookRepository.findById("978-4297156589").get().getPrice()) .isEqualTo(3970); // delete bookRepository.deleteById("978-4297136130"); assertThat(bookRepository.findAll().count()).isEqualTo(2); // deleteAll bookRepository.deleteAll(books); assertThat(bookRepository.findAll().count()).isZero(); }); }
それくらいですね…。
JAX-RSリソースクラスのテスト。こちらはクライアント側で、REST Assuredを使ってテストコードを書きます。
src/test/java/org/littlewings/jakarta/data/book/BookResourceIT.java
package org.littlewings.jakarta.data.book; import static io.restassured.RestAssured.given; import static org.assertj.core.api.Assertions.assertThat; import io.restassured.RestAssured; import io.restassured.common.mapper.TypeRef; 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.io.IOException; import java.net.URL; import java.nio.file.Path; import java.util.List; 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.jakarta.data.RestApplication; @ArquillianTest @RunAsClient class BookResourceIT { @ArquillianResource private URL deploymentUrl; private String resourcePrefix = RestApplication.class.getAnnotation(ApplicationPath.class).value().replaceFirst("^/", ""); @Deployment static WebArchive createDeployment() throws IOException { File mainResources = Path.of("src/main/resources").toFile(); return ShrinkWrap.create(WebArchive.class) .addPackages(true, RestApplication.class.getPackage()) .addAsResource(mainResources, ""); } @BeforeEach void setUp() { RestAssured.baseURI = deploymentUrl + resourcePrefix; RestAssured.config = RestAssuredConfig.config() .objectMapperConfig( ObjectMapperConfig.objectMapperConfig().defaultObjectMapperType(ObjectMapperType.JSONB)); given().when().delete("/books").then().assertThat().statusCode(Response.Status.NO_CONTENT.getStatusCode()); } @Test void registerAndFindSimply() { // insert var book1 = given().contentType(MediaType.APPLICATION_JSON) .body(Book.create("978-4774183169", "パーフェクト Java EE", 3520)) .when() .post("/books") .then() .assertThat() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON) .extract() .body() .as(Book.class); assertThat(book1.getTitle()).isEqualTo("パーフェクト Java EE"); // find one var foundBook1 = given().when() .get("/books/978-4774183169") .then() .assertThat() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON) .extract() .body() .as(Book.class); assertThat(foundBook1.getTitle()).isEqualTo("パーフェクト Java EE"); // insert given().contentType(MediaType.APPLICATION_JSON) .body(Book.create("978-4297156589", "作って学ぶ Spring Boot入門", 2970)) .when() .post("/books") .then() .assertThat() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON); // insert given().contentType(MediaType.APPLICATION_JSON) .body(Book.create("978-4297136130", "プロになるためのSpring入門 -- ゼロからの開発力養成講座", 3960)) .when() .post("/books") .then() .assertThat() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON); // findAll var allBooks = given().when() .get("/books") .then() .assertThat() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON) .extract() .body() .as(new TypeRef<List<Book>>() {}); assertThat(allBooks).hasSize(3); assertThat(allBooks.getFirst().getTitle()).isEqualTo("プロになるためのSpring入門 -- ゼロからの開発力養成講座"); assertThat(allBooks.get(1).getTitle()).isEqualTo("パーフェクト Java EE"); assertThat(allBooks.getLast().getTitle()).isEqualTo("作って学ぶ Spring Boot入門"); } @Test void updateAndDelete() { var books = List.of( Book.create("978-4774183169", "パーフェクト Java EE", 3520), Book.create("978-4297156589", "作って学ぶ Spring Boot入門", 2970), Book.create("978-4297136130", "プロになるためのSpring入門 -- ゼロからの開発力養成講座", 3960)); books.forEach(book -> given().contentType(MediaType.APPLICATION_JSON) .body(book) .when() .post("/books") .then() .assertThat() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON)); // update given().contentType(MediaType.APPLICATION_JSON) .body(Book.create("978-4297156589", "作って学ぶ Spring Boot入門", 4970)) .when() .put("/books/978-4297156589") .then() .assertThat() .statusCode(Response.Status.NO_CONTENT.getStatusCode()); // find one var foundBook = given().when() .get("/books/978-4297156589") .then() .assertThat() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON) .extract() .body() .as(Book.class); assertThat(foundBook.getTitle()).isEqualTo("作って学ぶ Spring Boot入門"); assertThat(foundBook.getPrice()).isEqualTo(4970); // delete given().when() .delete("/books/978-4297136130") .then() .assertThat() .statusCode(Response.Status.NO_CONTENT.getStatusCode()); // findAll var allBooks = given().when() .get("/books") .then() .assertThat() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON) .extract() .body() .as(new TypeRef<List<Book>>() {}); assertThat(allBooks).hasSize(2); assertThat(allBooks.getFirst().getTitle()).isEqualTo("作って学ぶ Spring Boot入門"); assertThat(allBooks.getLast().getTitle()).isEqualTo("パーフェクト Java EE"); } @Test void duplicate() { var book1 = given().contentType(MediaType.APPLICATION_JSON) .body(Book.create("978-4774183169", "パーフェクト Java EE", 3520)) .when() .post("/books") .then() .assertThat() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON) .extract() .body() .as(Book.class); assertThat(book1.getTitle()).isEqualTo("パーフェクト Java EE"); // find one var foundBook1 = given().when() .get("/books/978-4774183169") .then() .assertThat() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON) .extract() .body() .as(Book.class); assertThat(foundBook1.getTitle()).isEqualTo("パーフェクト Java EE"); given().contentType(MediaType.APPLICATION_JSON) .body(Book.create("978-4774183169", "パーフェクト Java EE", 3520)) .when() .post("/books") .then() .assertThat() .statusCode(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()); } @Test void findByPrice() { var books = List.of( Book.create("978-4774183169", "パーフェクト Java EE", 3520), Book.create("978-4297156589", "作って学ぶ Spring Boot入門", 2970), Book.create("978-4297136130", "プロになるためのSpring入門 -- ゼロからの開発力養成講座", 3960)); books.forEach(book -> given().contentType(MediaType.APPLICATION_JSON) .body(book) .when() .post("/books") .then() .assertThat() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON)); // findAll var resultBooks = given().param("greaterThanPrice", 3500) .param("lessThanPrice", 4000) .when() .get("/books/query") .then() .assertThat() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON) .extract() .body() .as(new TypeRef<List<Book>>() {}); assertThat(resultBooks).hasSize(2); assertThat(resultBooks.getFirst().getTitle()).isEqualTo("パーフェクト Java EE"); assertThat(resultBooks.getLast().getTitle()).isEqualTo("プロになるためのSpring入門 -- ゼロからの開発力養成講座"); } @Test void rollback() { var books = List.of( Book.create("978-4774183169", "パーフェクト Java EE", 3520), Book.create("978-4297156589", "作って学ぶ Spring Boot入門", 2970), Book.create("978-4297136130", "プロになるためのSpring入門 -- ゼロからの開発力養成講座", 3960)); books.forEach(book -> given().contentType(MediaType.APPLICATION_JSON) .body(book) .when() .post("/books") .then() .assertThat() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON)); var addBooks = List.of( Book.create("978-4798180946", "独習Java 第6版", 3278), Book.create("978-4295024422", "スッキリわかるJava入門 第5版 (スッキリわかる入門シリーズ)", 3080)); // rollback test given().contentType(MediaType.APPLICATION_JSON) .body(addBooks) .when() .post("/books/rollback") .then() .assertThat() .statusCode(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()); // findAll var allBooks = given().when() .get("/books") .then() .assertThat() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON) .extract() .body() .as(new TypeRef<List<Book>>() {}); assertThat(allBooks).hasSize(3); } }
確認はこんな感じで行います。
WildFlyのプロビジョニング、起動。
$ mvn package -DskipTests -Dwildfly.package.deployment.skip $ mvn wildfly:start
WildFlyのプロビジョニングをスキップして、テストを実行。
$ mvn verify -Dwildfly.package.skip
テストが終了したら、WildFlyを停止。
$ mvn wildfly:shutdown
おわりに
Jakarta Dataを使ったアプリケーションをArquillianでテストしてみました。
といっても、前回のエントリーを分割しただけなのですが。ただ、Arquillianを使ってテストするのはこれはこれで苦労したので
ちゃんとやっておいてよかったです…。