ããã¯ããªã«ãããããŠæžãããã®ïŒ
Quarkusã§ã®ãã¹ãã®ããæ¹ãæžãæ¹ãèŠããŠã¿ããããªãšããããšã§ã
ãã¡ãã®ã¬ã€ãã«æ²¿ã£ãŠãèŠãŠãããŸãã
Testing Your Application - Quarkus
ç°å¢
ä»åã®ç°å¢ã¯ããã¡ãã§ãã
$ java --version openjdk 11.0.10 2021-01-19 OpenJDK Runtime Environment (build 11.0.10+9-Ubuntu-0ubuntu1.20.04) OpenJDK 64-Bit Server VM (build 11.0.10+9-Ubuntu-0ubuntu1.20.04, mixed mode, sharing) $ mvn --version Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 11.0.10, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.4.0-66-generic", arch: "amd64", family: "unix"
Quarkusã¯ã1.12.1.Finalã䜿çšããŸãã
ãé¡
以äžã®ãé¡ã§è¡ããŸãã
- SmallRye Mutinyã䜿ã
- CDI管çBeanã®ãã¹ãããã
- JAX-RSãªãœãŒã¹ã¯ã©ã¹ã®ãã¹ãããã
- èšå®ãã¡ã€ã«ã®é ç®ãèªã¿èŸŒã¿ããã€ãã¹ãæã«å€ãåãæ¿ãã
- ãã€ãã£ãã€ã¡ãŒãžã¯å¯Ÿè±¡å€ãšãã
ãªã®ã§ããã¹ãã®ã¬ã€ã以å€ã«ã次ã®ãããªã¬ã€ããåç §ããŠæžããŠããŸãã
Getting Started With Reactive - Quarkus
Writing JSON REST Services - Quarkus
Configuring Your Application - Quarkus
Configuration Reference Guide - Quarkus
ãããžã§ã¯ããäœæãã
ã§ã¯ããŸãã¯ãããžã§ã¯ããäœæããŸãã
$ mvn io.quarkus:quarkus-maven-plugin:1.12.1.Final:create \ -DprojectGroupId=org.littlewings \ -DprojectArtifactId=resteasy-testing \ -DprojectVersion=0.0.1-SNAPSHOT \ -Dextensions="resteasy-mutiny,resteasy-jackson"
Extensionã¯ãresteasy-mutiny
ãresteasy-jackson
ã®2ã€ã«ããŸããã
----------- selected extensions: - io.quarkus:quarkus-resteasy-jackson - io.quarkus:quarkus-resteasy-mutiny applying codestarts... ð java ð§° maven ð quarkus ð config-properties ð dockerfiles ð maven-wrapper ð resteasy-jackson-example
äœæããããã£ã¬ã¯ããªå ã«ç§»åã
$ cd resteasy-testing
pom.xml
ã«æžãããŠããäŸåé¢ä¿ã¯ããã¡ãã§ãã
pom.xml
<dependencies> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-jackson</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-mutiny</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-arc</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-junit5</artifactId> <scope>test</scope> </dependency> </dependencies>
ãã¹ãåãã¯ãquarkus-junit5
ãæžãããŠããã®ã¿ã§ãã
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-junit5</artifactId> <scope>test</scope> </dependency>
ã¬ã€ããèŠããšãREST-assuredãšãããã®ã䜿ããããªã®ã§ãdependency
ã«è¿œå ããŸãã
Testing Your Application / Recap of HTTP based Testing in JVM mode
<dependency> <groupId>io.rest-assured</groupId> <artifactId>rest-assured</artifactId> <scope>test</scope> </dependency>
REST-assuredã¯Quarkusã®BOMã«å ¥ã£ãŠããã®ã§ãããŒãžã§ã³ã®æå®ã¯äžèŠã§ãã
https://github.com/quarkusio/quarkus/blob/1.12.1.Final/bom/application/pom.xml#L2681-L2704
src
ãã£ã¬ã¯ããªã®äžãèŠãŠã¿ãŸãã
$ find src -type f src/main/docker/Dockerfile.native-distroless src/main/docker/Dockerfile.jvm src/main/docker/Dockerfile.native src/main/docker/Dockerfile.legacy-jar src/main/resources/META-INF/resources/index.html src/main/resources/application.properties src/main/java/org/littlewings/resteasyjackson/JacksonResource.java src/main/java/org/littlewings/resteasyjackson/MyObjectMapperCustomizer.java
resteasyjackson
ãšããäžæè°ãªããã±ãŒãžããããŸããã
ãããã£ããã¡ã€ã«ãçæãããå ã¯ããã¡ãã®ããã§ãã
äžèº«ãèŠãŸããããä»åã¯èŠããªãæ°ãããã®ã§åé€ã
$ rm -rf src/main/java/org/littlewings/resteasyjackson
ãªããåé€ãããã¡ã€ã«ã®å ã¯ãã¡ãã«ãããŸãã
æ°ã«ãªãæ¹ã¯ãäžèº«ãã©ããã
ã¢ããªã±ãŒã·ã§ã³ãæžã
ã§ã¯ããã¹ã察象ãšãªãã¢ããªã±ãŒã·ã§ã³ãæžããŸãããã
ãé¡ã¯æžç±ãšããããšã§ã
src/main/java/org/littlewings/testing/entity/Book.java
package org.littlewings.testing.entity; public class Book { String isbn; String title; int price; public static Book create(String isbn, String title, int price) { Book book = new Book(); book.setIsbn(isbn); book.setTitle(title); book.setPrice(price); return book; } // getterïŒsetterã¯çç¥ }
ãã¡ããæ°žç¶åããŠä¿æãããã®ãæã¡ãããšããã§ãããä»åã¯ConcurrentHashMap
ã§æã€ããšã«ããŸãã
ãã®ã¯ã©ã¹ã¯ãCDI管çBeanãšããŠå®çŸ©ããŸãã
src/main/java/org/littlewings/testing/repository/InMemoryBookRepository.java
package org.littlewings.testing.repository; import java.util.Comparator; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.stream.Collectors; import javax.enterprise.context.ApplicationScoped; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import org.littlewings.testing.entity.Book; @ApplicationScoped public class InMemoryBookRepository { ConcurrentMap<String, Book> books = new ConcurrentHashMap<>(); public Uni<Book> insert(Book book) { return Uni.createFrom().item(books.put(book.getIsbn(), book)).map(b -> book); } public Uni<Book> findByIsbn(String isbn) { return Uni.createFrom().item(books.get(isbn)); } public Multi<Book> findAll() { return Multi .createFrom() .iterable(books.values().stream().sorted(Comparator.comparingInt(Book::getPrice).reversed()).collect(Collectors.toList())); } public Uni<Integer> size() { return Uni.createFrom().item(books.size()); } public Uni<Book> delete(String isbn) { return Uni.createFrom().item(books.remove(isbn)); } public Uni<Void> clear() { return Uni.createFrom().voidItem().onItem().invoke(() -> books.clear()); } }
2ã€ã®ã¯ã©ã¹ã䜿çšãããJAX-RSãªãœãŒã¹ã¯ã©ã¹ãç°¡åãªèªã¿æžããã§ããã ãã§ããã
src/main/java/org/littlewings/testing/rest/BookResource.java
package org.littlewings.testing.rest; import javax.inject.Inject; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import org.littlewings.testing.entity.Book; import org.littlewings.testing.repository.InMemoryBookRepository; @Path("book") public class BookResource { @Inject InMemoryBookRepository bookRepository; @GET @Path("{isbn}") @Produces(MediaType.APPLICATION_JSON) public Uni<Book> find(@PathParam("isbn") String isbn) { return bookRepository.findByIsbn(isbn); } @GET @Produces(MediaType.APPLICATION_JSON) public Multi<Book> findAll() { return bookRepository.findAll(); } @PUT @Path("{isbn}") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Uni<Book> put(@PathParam("isbn") String isbn, Book book) { return bookRepository.insert(book); } @DELETE @Path("{isbn}") @Produces(MediaType.APPLICATION_JSON) public Uni<Response> delete(@PathParam("isbn") String isbn) { return bookRepository .delete(isbn) .onItem() .transform(b -> b != null ? Response.Status.NO_CONTENT : Response.Status.NOT_FOUND) .onItem() .transform(status -> Response.status(status).build()); } }
èšå®ãã¡ã€ã«ã«é ç®ãå®çŸ©ããŸãããã
src/main/resources/application.properties
app.config.message1=Hello World!! app.config.message2=Hello Quarkus!! app.config.message3=Wow!!
å®çŸ©ããé ç®ãè¿ãJAX-RSãªãœãŒã¹ã¯ã©ã¹ã
src/main/java/org/littlewings/testing/rest/ConfigResource.java
package org.littlewings.testing.rest; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.eclipse.microprofile.config.inject.ConfigProperty; @Path("config") public class ConfigResource { @ConfigProperty(name = "app.config.message1") String message1; @ConfigProperty(name = "app.config.message2") String message2; @ConfigProperty(name = "app.config.message3") String message3; @GET @Path("message1") @Produces(MediaType.TEXT_PLAIN) public String message1() { return message1; } @GET @Path("message2") @Produces(MediaType.TEXT_PLAIN) public String message2() { return message2; } @GET @Path("message3") @Produces(MediaType.TEXT_PLAIN) public String message3() { return message3; } }
軜ããåäœç¢ºèªããŸãããã
$ mvn package $ java -jar target/quarkus-app/quarkus-run.jar
ããŒã¿ã®ç»é²ã
$ curl -XPUT -H 'Content-Type: application/json' localhost:8080/book/978-4295008477 -d '{ "isbn": "978-4295008477", "title": "æ°äžä»£Javaããã°ã©ãã³ã°ã¬ã€ã[Java SE 10/11/12/13ãšèšèªæ¡åŒµãããžã§ã¯ã]", "price": 2860 }' {"isbn":"978-4295008477","title":"æ°äžä»£Javaããã°ã©ãã³ã°ã¬ã€ã[Java SE 10/11/12/13ãšèšèªæ¡åŒµãããžã§ã¯ã]","price":2860}k
ååŸã
$ curl localhost:8080/book/978-4295008477 {"isbn":"978-4295008477","title":"æ°äžä»£Javaããã°ã©ãã³ã°ã¬ã€ã[Java SE 10/11/12/13ãšèšèªæ¡åŒµãããžã§ã¯ã]","price":2860} $ curl localhost:8080/book [{"isbn":"978-4295008477","title":"æ°äžä»£Javaããã°ã©ãã³ã°ã¬ã€ã[Java SE 10/11/12/13ãšèšèªæ¡åŒµãããžã§ã¯ã]","price":2860}]
èšå®ãã¡ã€ã«ã®é ç®ååŸã
$ curl localhost:8080/config/message1 Hello World!! $ curl localhost:8080/config/message2 Hello Quarkus!! $ curl localhost:8080/config/message3 Wow!!
OKã§ãããã§ã¯ããããã®ãã¹ããæžããŠãããŸãããã
Quakusã§ã®ãã¹ããæžã
Quarkusã§ã®ãã¹ãã«é¢ããã¬ã€ãã¯ããã¡ãã«ãªããŸãã
Testing Your Application - Quarkus
MavenäŸåé¢ä¿ãšããŠquarkus-junit5
ã¯å¿
é ã§ãrest-assured
ã¯HTTPã«é¢ãããã¹ããè¡ãå Žåã«å¿
èŠã«å¿ããŠè¿œå ããšããæãã§ããã
ãŸããMaven Surefire Pluginã®èšå®ãšããŠãJBoss Log Managerã®èšå®ãå
¥ããããã«ããŸãããšãã£ãŠãMavenãããžã§ã¯ãã
äœã£ã段éã§ãã®èšå®ã¯å
¥ã£ãŠããŸããã
<plugin> <artifactId>maven-failsafe-plugin</artifactId> <version>${surefire-plugin.version}</version> <executions> <execution> <goals> <goal>integration-test</goal> <goal>verify</goal> </goals> <configuration> <systemPropertyVariables> <native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path> <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager> <maven.home>${maven.home}</maven.home> </systemPropertyVariables> </configuration> </execution> </executions> </plugin>
å®éã«ãã¹ããæžããŠããéã«åºæ¬ãšãªãã®ã¯ã@QuarkusTest
ãšããã¢ãããŒã·ã§ã³ã®ããã§ãã
CDI管çBeanã®ãã¹ããæžã
ã¬ã€ãã«æžãããŠããé ãšã¯ç°ãªããŸãããæåã«CDI管çBeanã®ãã¹ããæžããŠã¿ãŸãããã
Testing Your Application / Injection into tests
äœæãããã¹ãã³ãŒãã¯ããã¡ãã
src/test/java/org/littlewings/testing/repository/InMemoryBookRepositoryTest.java
package org.littlewings.testing.repository; import java.util.List; import javax.inject.Inject; import io.quarkus.test.junit.QuarkusTest; import io.smallrye.mutiny.helpers.test.AssertSubscriber; import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.littlewings.testing.entity.Book; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @QuarkusTest public class InMemoryBookRepositoryTest { @Inject InMemoryBookRepository bookRepository; @BeforeEach public void setup() { List.of( Book.create("978-4295008477", "æ°äžä»£Javaããã°ã©ãã³ã°ã¬ã€ã[Java SE 10/11/12/13ãšèšèªæ¡åŒµãããžã§ã¯ã]", 2860), Book.create("978-4295008583", "ãã€ã¯ããµãŒãã¹ãã¿ãŒã³[å®è·µçã·ã¹ãã ãã¶ã€ã³ã®ããã®ã³ãŒã解説]", 5280), Book.create("978-1492062653", "Quarkus Cookbook: Kubernetes-optimized Java Solutions", 6376), Book.create("978-4295007753", "ã¯ã©ãŠããã€ãã£ãã»ã¢ãŒããã¯ã㣠å¯çšæ§ãšè²»çšå¯Ÿå¹æã極ããæ¬¡äžä»£èšèšã®åå", 4290) ).forEach(b -> bookRepository.insert(b).subscribe().with(v -> { })); } @AfterEach public void teardown() { bookRepository.clear().subscribe().with(v -> { }); } @Test public void findByIsbnTest() { UniAssertSubscriber<Book> subscriber = bookRepository.findByIsbn("978-4295008477").subscribe().withSubscriber(UniAssertSubscriber.create()); Book book = subscriber .assertCompleted() .getItem(); assertThat(book.getIsbn(), is("978-4295008477")); assertThat(book.getTitle(), is("æ°äžä»£Javaããã°ã©ãã³ã°ã¬ã€ã[Java SE 10/11/12/13ãšèšèªæ¡åŒµãããžã§ã¯ã]")); assertThat(book.getPrice(), is(2860)); } @Test public void findAllTest() { UniAssertSubscriber<List<String>> isbnSubscriber = bookRepository .findAll() .map(Book::getIsbn) .collect() .asList() .subscribe() .withSubscriber(UniAssertSubscriber.create()); isbnSubscriber .assertCompleted() .assertItem( List.of( "978-1492062653", // price: 6379 "978-4295008583", // price: 5280 "978-4295007753", // price: 4290 "978-4295008477" // price: 2860 ) ); AssertSubscriber<String> subscriber = bookRepository .findAll() .map(Book::getIsbn) .subscribe() .withSubscriber(AssertSubscriber.create(10)); subscriber .assertCompleted() .assertItems( "978-1492062653", // price: 6379 "978-4295008583", // price: 5280 "978-4295007753", // price: 4290 "978-4295008477" // price: 2860 ); } @Test public void putTest() { bookRepository .insert(Book.create("978-4295009795", "Kuberneteså®å šã¬ã€ã 第2ç", 4400)) .subscribe() .withSubscriber(UniAssertSubscriber.create()); UniAssertSubscriber<Integer> subscriber = bookRepository.size().subscribe().withSubscriber(UniAssertSubscriber.create()); subscriber.assertCompleted().assertItem(5); } @Test public void deleteTest() { bookRepository .delete("978-4295007753") .subscribe() .withSubscriber(UniAssertSubscriber.create()) .assertCompleted(); UniAssertSubscriber<Integer> subscriber = bookRepository.size().subscribe().withSubscriber(UniAssertSubscriber.create()); subscriber.assertCompleted().assertItem(3); } }
ãã¹ãã¯ã©ã¹ã«ã¯ã@QuarkusTest
ã¢ãããŒã·ã§ã³ãä»äžããŸãã
@QuarkusTest public class InMemoryBookRepositoryTest {
ãã®ç¶æ
ã§ãCDI管çBeanããµã€ãã«@Inject
ããããšãã§ããŸãã
@Inject
InMemoryBookRepository bookRepository;
InMemoryBookRepository
ã¯ã©ã¹ã§æã€ããŒã¿ã¯ããã¹ãã®åºŠã«åæããŒã¿ç»é²ãåé€ããããã«ããŠããŸãã
@Inject InMemoryBookRepository bookRepository; @BeforeEach public void setup() { List.of( Book.create("978-4295008477", "æ°äžä»£Javaããã°ã©ãã³ã°ã¬ã€ã[Java SE 10/11/12/13ãšèšèªæ¡åŒµãããžã§ã¯ã]", 2860), Book.create("978-4295008583", "ãã€ã¯ããµãŒãã¹ãã¿ãŒã³[å®è·µçã·ã¹ãã ãã¶ã€ã³ã®ããã®ã³ãŒã解説]", 5280), Book.create("978-1492062653", "Quarkus Cookbook: Kubernetes-optimized Java Solutions", 6376), Book.create("978-4295007753", "ã¯ã©ãŠããã€ãã£ãã»ã¢ãŒããã¯ã㣠å¯çšæ§ãšè²»çšå¯Ÿå¹æã極ããæ¬¡äžä»£èšèšã®åå", 4290) ).forEach(b -> bookRepository.insert(b).subscribe().with(v -> { })); } @AfterEach public void teardown() { bookRepository.clear().subscribe().with(v -> { }); }
ãã®ç¯å²ã ãšãåºæ¬çã«ã¯JUnit 5ã䜿ã£ããã¹ãã®è©±ãªã®ã§ãããSmallRye Mutiniyã«é¢ãããã¹ãã«é¢ããŠã ãå°ãæžããŠ
ãããŸãããã
SmallRye Mutinyã«ãã¹ãã«é¢ããæ å ±ã¯ããã¡ãã«æžãããŠããŸãã
How can I write unit / integration tests?
Uni
ã«å¯Ÿãããã¹ãã®å Žåã¯ãUniAssertSubscriber
ã䜿ããŸãã
@Test public void findByIsbnTest() { UniAssertSubscriber<Book> subscriber = bookRepository.findByIsbn("978-4295008477").subscribe().withSubscriber(UniAssertSubscriber.create()); Book book = subscriber .assertCompleted() .getItem(); assertThat(book.getIsbn(), is("978-4295008477")); assertThat(book.getTitle(), is("æ°äžä»£Javaããã°ã©ãã³ã°ã¬ã€ã[Java SE 10/11/12/13ãšèšèªæ¡åŒµãããžã§ã¯ã]")); assertThat(book.getPrice(), is(2860)); }
Multi
ã®å Žåã¯ãAssertSubscriber
ã䜿ããŸãã以äžã¯ãfindAll
ã®çµæã1床Uni
ã«å€æããŠã¢ãµãŒã·ã§ã³ããŠãããã®ãšã
Multi
ã®ãŸãŸã¢ãµãŒã·ã§ã³ããŠãããã®ã§ãã
@Test public void findAllTest() { UniAssertSubscriber<List<String>> isbnSubscriber = bookRepository .findAll() .map(Book::getIsbn) .collect() .asList() .subscribe() .withSubscriber(UniAssertSubscriber.create()); isbnSubscriber .assertCompleted() .assertItem( List.of( "978-1492062653", // price: 6379 "978-4295008583", // price: 5280 "978-4295007753", // price: 4290 "978-4295008477" // price: 2860 ) ); AssertSubscriber<String> subscriber = bookRepository .findAll() .map(Book::getIsbn) .subscribe() .withSubscriber(AssertSubscriber.create(10)); subscriber .assertCompleted() .assertItems( "978-1492062653", // price: 6379 "978-4295008583", // price: 5280 "978-4295007753", // price: 4290 "978-4295008477" // price: 2860 ); }
AssertSubscriber
ã䜿ãæã¯ãcreate
ã®åŒæ°ã«ãªã¯ãšã¹ãããæ°ãæžããŠãããªããšãçç¥ãããš0
ãæå®ããããšã«ãªã£ãŠ
äžåSubscribeããŠãããŸãããæåãããã«ããããŸããâŠã
AssertSubscriber<String> subscriber =
bookRepository
.findAll()
.map(Book::getIsbn)
.subscribe()
.withSubscriber(AssertSubscriber.create(10));
CDI管çBeanã®ãã¹ãã¯ããããªæãã§ããã
ã¡ãªã¿ã«ã@Transactional
ã¢ãããŒã·ã§ã³ããã¹ãã§äœ¿ãããšãã§ããããã§ãããèªåã¯SmallRye Mutinyãäžå¿ã«æ±ãäºå®ãªã®ã§ã
ãã®æ©èœã®åºçªã¯ãªãããã§ãã
â»ãããšããMicroProfile Context Propagationã䜿ãã°ãããã§ããããïŒ
Testing Your Application / Tests and Transactions
ããã«ããŠããSmallRye Mutinyã®ãµã€ãã以åããã ãã¶é°å²æ°ãå€ãããŸãããâŠã
ãã¹ãå ã§Quarkusã«ã¢ã¯ã»ã¹ããURLãååŸãã
ç¶ããŠã¯ããã¡ãã§ãã
Testing Your Application / Injecting a URI
Testing Your Application / TestHTTPResource
@TestHTTPResource
ãšããã¢ãããŒã·ã§ã³ã䜿çšãããšãQuarkusãžã¢ã¯ã»ã¹ããããã®URLãã€ã³ãžã§ã¯ã·ã§ã³ã§ããŸãã
äœ¿ãæ¹ã¯ããããªæãã§ãã
src/test/java/org/littlewings/testing/rest/InjectUrlTest.java
package org.littlewings.testing.rest; import java.net.URL; import io.quarkus.test.common.http.TestHTTPEndpoint; import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.test.junit.QuarkusTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @QuarkusTest public class InjectUrlTest { @TestHTTPResource URL rootUrl; @TestHTTPResource("book") URL pathSpecificUrl; @TestHTTPEndpoint(BookResource.class) @TestHTTPResource URL resourceClassSpecificUrl; @Test public void injectedUrlTest() { Assertions.assertEquals("http://localhost:8083/", rootUrl.toString()); Assertions.assertEquals("http://localhost:8083/book", pathSpecificUrl.toString()); Assertions.assertEquals("http://localhost:8083/book", resourceClassSpecificUrl.toString()); } }
@QuarkusTest
ã¢ãããŒã·ã§ã³ãä»äžããã¯ã©ã¹ã«å¯ŸããŠ
@QuarkusTest public class InjectUrlTest {
@TestHTTPResource
ã¢ãããŒã·ã§ã³ãä»äžããURLã宣èšãããšãQuarkusãžã¢ã¯ã»ã¹ã§ããURLïŒhttp://localhost:[port]
ïŒã
ååŸã§ããŸãã
@TestHTTPResource
URL rootUrl;
ãã¹ãæå®ããããšãã§ããŸãã
@TestHTTPResource("book") URL pathSpecificUrl;
@TestHTTPEndpoint
ã¢ãããŒã·ã§ã³ã«JAX-RSãªãœãŒã¹ã¯ã©ã¹ãæå®ãããšãJAX-RSãªãœãŒã¹ã¯ã©ã¹ã«æå®ããã
@Path
ã¢ãããŒã·ã§ã³ã®å€ãåããŠãããŸãã
@TestHTTPEndpoint(BookResource.class) @TestHTTPResource URL resourceClassSpecificUrl;
ãã®ããããã®æ©èœã¯@Path
ã¢ãããŒã·ã§ã³ã«äŸåããŠãããJAX-RSãªãœãŒã¹ã¯ã©ã¹ã«@Path
ã¢ãããŒã·ã§ã³ãä»äžããªãã£ãå Žåã¯
//@Path("book") public class BookResource {
ãã¹ãå®è¡æã«ãšã©ãŒã«ãªããŸãã
Caused by: java.lang.RuntimeException: Could not determine the endpoint path for class org.littlewings.testing.rest.BookResource to inject java.net.URL org.littlewings.testing.rest.UrlBookResourceTest.resourceClassSpecificUrl at io.quarkus.test.common.http.TestHTTPResourceManager.inject(TestHTTPResourceManager.java:78)
ãã®3ãã¿ãŒã³ã§ã@TestHTTPResource
ã¢ãããŒã·ã§ã³ã䜿ã£ãŠã€ã³ãžã§ã¯ã·ã§ã³ããURLã®çµæã¯ä»¥äžã«ãªããŸãã
@Test public void injectedUrlTest() { Assertions.assertEquals("http://localhost:8083/", rootUrl.toString()); Assertions.assertEquals("http://localhost:8083/book", pathSpecificUrl.toString()); Assertions.assertEquals("http://localhost:8083/book", resourceClassSpecificUrl.toString()); }
éåžžã¯ããã®URLã䜿ã£ãŠãã¹ãã³ãŒããæžããŠããããã§ãããä»åã¯ãããŸããã
ãšããã§ãQuakursã§ã®ãã¹ãæã«äœ¿ãããããã©ã«ãã®ããŒãã¯8081
ãªã®ã§ãããä»åãã¡ãã®èšå®ã䜿ã£ãŠå€æŽããŠããŸãã
Testing Your Application / Controlling the test port
ä»åã¯ä»¥äžã®ããã«å®çŸ©ããŠããã®ã§ããããããã©ãã§å®çŸ©ããŠãããã¯ãŸãåŸã§æžããŸãã
quarkus.http.test-port=8083
ãšãããããä»åã®ãã¹ãã®éã¯ããã¹ãã«ãããQuarkusã®ãªãã¹ã³ããŒãã¯8083
ãšãªããŸãã
REST-assuredã䜿ã£ãŠãã¹ããã
å
ã»ã©ã¯@TestHTTPResource
ã¢ãããŒã·ã§ã³ã䜿ã£ãŠãQuarkusã«ã¢ã¯ã»ã¹ããããã®URLãååŸããŸãããããã®æ¹æ³ã ãš
HTTPã®ãã¹ãã«é¢ãããµããŒãããªã«ããããŸããã
ãã£ãšæœè±¡åºŠã®é«ããã¹ãã®æ¹æ³ãšããŠãQuarkusã§ã¯REST-assuredã䜿ãããšãã§ããŸãã
Testing Your Application / RESTassured
@TestHTTPEndpoint
ãšçµã¿åãããŠäœ¿ãããšã§ãJAX-RSãªãœãŒã¹ã¯ã©ã¹ãžã¢ã¯ã»ã¹ãããã¹ããæžãããããªããŸãã
REST-assuredèªäœã¯ç¬ç«ããREST APIåãã®ãã¹ãã©ã€ãã©ãªã§ãã
äœ¿ãæ¹ã¯ãRestAssured
ã®JavadocãWikiãèŠããšã ãããããããŸãã
RestAssured - rest-assured 4.3.3 javadoc
Usage · rest-assured/rest-assured Wiki · GitHub
äœæãããã¹ãã³ãŒãã¯ããã¡ãã
src/test/java/org/littlewings/testing/rest/BookResourceTest.java
package org.littlewings.testing.rest; import java.util.List; import javax.inject.Inject; import io.quarkus.test.common.http.TestHTTPEndpoint; import io.quarkus.test.junit.QuarkusTest; import org.apache.http.entity.ContentType; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.littlewings.testing.entity.Book; import org.littlewings.testing.repository.InMemoryBookRepository; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.hasSize; @QuarkusTest @TestHTTPEndpoint(BookResource.class) public class BookResourceTest { @Inject InMemoryBookRepository bookRepository; @BeforeEach public void setup() { List.of( Book.create("978-4295008477", "æ°äžä»£Javaããã°ã©ãã³ã°ã¬ã€ã[Java SE 10/11/12/13ãšèšèªæ¡åŒµãããžã§ã¯ã]", 2860), Book.create("978-4295008583", "ãã€ã¯ããµãŒãã¹ãã¿ãŒã³[å®è·µçã·ã¹ãã ãã¶ã€ã³ã®ããã®ã³ãŒã解説]", 5280), Book.create("978-1492062653", "Quarkus Cookbook: Kubernetes-optimized Java Solutions", 6376), Book.create("978-4295007753", "ã¯ã©ãŠããã€ãã£ãã»ã¢ãŒããã¯ã㣠å¯çšæ§ãšè²»çšå¯Ÿå¹æã極ããæ¬¡äžä»£èšèšã®åå", 4290) ).forEach(b -> bookRepository.insert(b).subscribe().with(v -> { })); } @AfterEach public void teardown() { bookRepository.clear().subscribe().with(v -> { }); } @Test public void findByIsbnTest() { given() .pathParam("isbn", "978-4295008477") .when() .get("{isbn}") .then() .assertThat() .statusCode(200) .body("title", is("æ°äžä»£Javaããã°ã©ãã³ã°ã¬ã€ã[Java SE 10/11/12/13ãšèšèªæ¡åŒµãããžã§ã¯ã]")) .body("price", is(2860)); } @Test public void findAllTest() { given() .when() .get() .then() .assertThat() .statusCode(200) .body("price", is(List.of(6376, 5280, 4290, 2860))); } @Test public void putTest() { given() .pathParam("isbn", "978-4295009795") .contentType(ContentType.APPLICATION_JSON.getMimeType()) .body(Book.create("978-4295009795", "Kuberneteså®å šã¬ã€ã 第2ç", 4400)) .when() .put("{isbn}") .then() .assertThat() .statusCode(200); given() .when() .get() .then() .assertThat() .statusCode(200) .body("price", hasSize(5)); // 4 + 1 } @Test public void deleteTest() { given() .pathParam("isbn", "978-4295008583") .when() .delete("{isbn}") .then() .assertThat() .statusCode(204); given() .when() .get() .then() .assertThat() .statusCode(200) .body("price", hasSize(3)); // 4 - 1 } }
ãã¹ãã¯ã©ã¹ã«ã@QuarkusTest
ã¢ãããŒã·ã§ã³ãšããã¹ã察象ã®JAX-RSãªãœãŒã¹ã¯ã©ã¹ãæå®ããŠ@TestHTTPEndpoint
ã¢ãããŒã·ã§ã³ãä»äžããŸãã
@QuarkusTest @TestHTTPEndpoint(BookResource.class) public class BookResourceTest {
ãã¹ãããŒã¿ã¯ãCDI管çBeanã®ãã¹ãã®æãšåæ§ããã¹ãããšã«ç»é²ãåé€ããããã«ããŠããŸãã
@Inject InMemoryBookRepository bookRepository; @BeforeEach public void setup() { List.of( Book.create("978-4295008477", "æ°äžä»£Javaããã°ã©ãã³ã°ã¬ã€ã[Java SE 10/11/12/13ãšèšèªæ¡åŒµãããžã§ã¯ã]", 2860), Book.create("978-4295008583", "ãã€ã¯ããµãŒãã¹ãã¿ãŒã³[å®è·µçã·ã¹ãã ãã¶ã€ã³ã®ããã®ã³ãŒã解説]", 5280), Book.create("978-1492062653", "Quarkus Cookbook: Kubernetes-optimized Java Solutions", 6376), Book.create("978-4295007753", "ã¯ã©ãŠããã€ãã£ãã»ã¢ãŒããã¯ã㣠å¯çšæ§ãšè²»çšå¯Ÿå¹æã極ããæ¬¡äžä»£èšèšã®åå", 4290) ).forEach(b -> bookRepository.insert(b).subscribe().with(v -> { })); } @AfterEach public void teardown() { bookRepository.clear().subscribe().with(v -> { }); }
ããšã¯ããããªæãã§JAX-RSãªãœãŒã¹ã¯ã©ã¹ãžHTTPãªã¯ãšã¹ããå®è¡ããã¢ãµãŒã·ã§ã³ããããšãã§ããŸãã
@Test public void findByIsbnTest() { given() .pathParam("isbn", "978-4295008477") .when() .get("{isbn}") .then() .assertThat() .statusCode(200) .body("title", is("æ°äžä»£Javaããã°ã©ãã³ã°ã¬ã€ã[Java SE 10/11/12/13ãšèšèªæ¡åŒµãããžã§ã¯ã]")) .body("price", is(2860)); }
REST-assuredã¯åããŠäœ¿ã£ãã®ã§ãããbody
ã§JSONã®ãã¹ãæå®ããŠã¢ãµãŒã·ã§ã³ãããã§ããŠäŸ¿å©ã§ããã
ãã¹ã«ãããããèŠçŽ ãè€æ°ãã£ãå Žåã¯ãã³ã¬ã¯ã·ã§ã³ãšããŠã¢ãµãŒã·ã§ã³ã§ããŸãã
@Test public void findAllTest() { given() .when() .get() .then() .assertThat() .statusCode(200) .body("price", is(List.of(6376, 5280, 4290, 2860))); }
ãã¹ãæã«èšå®ãå€ãã
æåŸã¯ããã¹ãæã«èšå®ãã¡ã€ã«ã§æå®ããããããã£ã®å€ã倿ŽããŠã¿ãŸãããã
ããšããšãQuarkusã®ããã©ã«ãã®èšå®ãã¡ã€ã«ã«ä»¥äžã®å®çŸ©ãæžããŠããŸããã
src/main/resources/application.properties
app.config.message1=Hello World!! app.config.message2=Hello Quarkus!! app.config.message3=Wow!!
ãã¹ãæã¯ãã®å€ã®äžéšã倿Žãããããšããã·ãã¥ãšãŒã·ã§ã³ãèããŠã¿ãŸãã
ããã¥ã¡ã³ããèŠãŠãããšãã©ãããProfileãšãããã®ãããããã§ãã
Testing Your Application / Testing Different Profiles
ãã¹ããå®è¡ããŠã¿ããš
$ mvn test
ã©ãããtest
ãšããProfileã«ãªã£ãŠããããã§ãã
2021-03-07 00:42:24,190 INFO [io.quarkus] (main) Quarkus 1.12.1.Final on JVM started in 2.173s. Listening on: http://localhost:8083 2021-03-07 00:42:24,196 INFO [io.quarkus] (main) Profile test activated. 2021-03-07 00:42:24,197 INFO [io.quarkus] (main) Installed features: [cdi, mutiny, resteasy, resteasy-jackson, resteasy-mutiny, smallrye-context-propagation
ããã§ã以äžã®ããã¥ã¡ã³ããèŠãŠã¿ãŸãã
Configuring Your Application / Configuration Profiles
ããã©ã«ãã§ã¯ã3ã€ã®Profileãããããã§ãã
- dev - Activated when in development mode (i.e. quarkus:dev)
- test - Activated when running tests
- prod - The default profile when not running in development or test mode
ãããŠã%Profileå.[ããããã£]
ãšããèšèŒã§å¯Ÿè±¡ã®Profileã§åäœããŠããæã«æå¹ãªããããã£ãèšå®ã§ããããã§ãã
ããšãã°ããããªæãã§ããã
â»å®éã«ã¯ããã®ããã«ã¯å€æŽããŠããŸããïŒäŸã§ãïŒ
src/main/resources/application.properties
app.config.message1=Hello World!! app.config.message2=Hello Quarkus!! app.config.message3=Wow!! %test.app.config.message2=Test Hello Quarkus!!
ããã§ãããã®ã§ããããã¹ãçšã®èšå®ãsrc/main/resources
ã®æ¹ã«æžãã®ã¯ã¡ãã£ãšãªããšã
ãªã®ã§ããã¹ãçšã®èšå®ãã¡ã€ã«ãäœæããããšæããŸããConfiguration for MicroProfileã®APIã§ãã«ã¹ã¿ã ã®èšå®ãã¡ã€ã«ã
远å ããããšãã§ããŸãã
Configuration Reference / Custom configuration sources
GitHub - eclipse/microprofile-config: MicroProfile Configuration Feature
ãããªèšå®ãã¡ã€ã«ã«ããŸããã
src/test/resources/test-application.properties
config_ordinal = 300 quarkus.http.test-port=8083 %test.app.config.message2=Test Hello Quarkus!! app.config.message3=Oops!!
ãã®èšå®ãã¡ã€ã«ãèªããããªConfigSourceProvider
ã€ã³ã¿ãŒãã§ãŒã¹ã®å®è£
ã¯ã©ã¹ãäœæããŠ
src/test/java/org/littlewings/testing/config/TestApplicationPropertiesConfigSourceProvider.java
package org.littlewings.testing.config; import java.io.IOException; import java.io.UncheckedIOException; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import io.smallrye.config.PropertiesConfigSource; import org.eclipse.microprofile.config.spi.ConfigSource; import org.eclipse.microprofile.config.spi.ConfigSourceProvider; public class TestApplicationPropertiesConfigSourceProvider implements ConfigSourceProvider { @Override public Iterable<ConfigSource> getConfigSources(ClassLoader forClassLoader) { return List .of("test-application.properties") .stream() .map(forClassLoader::getResource) .filter(Objects::nonNull) .map(path -> { try { return new PropertiesConfigSource(path); } catch (IOException e) { throw new UncheckedIOException(e); } }) .collect(Collectors.toList()); } }
以äžã®ãã¡ã€ã«ãäœããäœæããConfigSourceProvider
ã€ã³ã¿ãŒãã§ãŒã¹ã®å®è£
ã¯ã©ã¹åãæžããŠãããŸãã
src/test/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSourceProvider
org.littlewings.testing.config.TestApplicationPropertiesConfigSourceProvider
ãŸãã远å ããèšå®ãã¡ã€ã«ã«æžããŠããconfig_ordinal
ãšããå€ã¯ãåªå
床ã§ãã
src/test/resources/test-application.properties
config_ordinal = 300 quarkus.http.test-port=8083 %test.app.config.message2=Test Hello Quarkus!! app.config.message3=Oops!!
ä»åã%test.app.config.message2
ãšããProfileå¥ã®æå®ãšãapplication.properties
ã§ã®å®çŸ©ãšãŸã£ããåãããŒã§ãã
app.config.message3
ã䜿ããäž¡æ¹ãšãapplication.properties
ããåªå
ãããããšã確èªããŸãã
application.properties
èªäœãã©ã®ãããªåªå
床ã«ãªã£ãŠãããã§ãããããã¯ãœãŒã¹ã³ãŒããèŠããšããããŸãã
JARãã¡ã€ã«ã®äžã®å Žåã250ã
META-INF/microprofile-config.properties
ãã¡ã€ã«ãšããŠç»é²ããå Žåã240ã
ãã¡ã€ã«ã·ã¹ãã äžã®å Žåã260ã
ãªã®ã§ãæ°ããäœæããtest-application.properties
ãã¡ã€ã«ã®åªå
床ã¯config_ordinal = 300
ãšããapplication.propreties
ããã
é«ãèšå®ããŠããŸãã
ããšã¯ããã¹ããæžãã ãã§ãããã¡ãããREST-assuredã䜿ã£ãŠãã¹ããæžããŸãã
src/test/java/org/littlewings/testing/rest/ConfigResourceTest.java
package org.littlewings.testing.rest; import io.quarkus.test.common.http.TestHTTPEndpoint; import io.quarkus.test.junit.QuarkusTest; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; @QuarkusTest @TestHTTPEndpoint(ConfigResource.class) public class ConfigResourceTest { @Test public void message1Test() { given() .when() .get("message1") .then() .assertThat() .statusCode(200) .body(is("Hello World!!")); } @Test public void message2Test() { given() .when() .get("message2") .then() .assertThat() .statusCode(200) .body(is("Test Hello Quarkus!!")); } @Test public void message3Test() { given() .when() .get("message3") .then() .assertThat() .statusCode(200) .body(is("Oops!!")); } }
ãã®ãã¹ãã«æžããéããtest-application.properties
ã§å®çŸ©ããããããã£ã«ã€ããŠã¯ããã¡ãã®æ¹ãåªå
ãããŠããããšã
確èªã§ããŸãã
ä»å確èªããå 容ã¯ããããªæãã§ãã
ãã¹ãã«é¢ãã話é¡ã§ãæ±ããªãã£ãããš
ãã¹ãã®ã¬ã€ãã«èŒã£ãŠããŠãä»åæ±ããªãã£ãã®ã¯ãã®ãããã§ãã
Testing Your Application / Applying Interceptors to Tests
Testing Your Application / Tests and Transactions
Testing Your Application / Enrichment via QuarkusTest*Callback
Testing Your Application / Testing Different Profiles
Testing Your Application / Mock Support
Testing Your Application / Starting services before the Quarkus application starts
Testing Your Application / Native Executable Testing
Testing Your Application / Running @QuarkusTest from an IDE
äžéšãå°ãè§Šãããã®ããããŸãããã ããããããªæãã§ãã
ã¢ãã¯ããã©ã³ã¶ã¯ã·ã§ã³ãProfileããã¹ãã®åã«å¥ã®ãµãŒãã¹ãåããããããã¯ãã®ãã¡äœ¿ãããšã«ãªããããªããšæã£ããã
ä»åã¯ããããªãšããã§ãããŸãã