ããã¯ããªã«ãããããŠæžãããã®ïŒ
Quarkusã§ã®ãã¹ãã®ããæ¹ãæžãæ¹ãèŠããŠã¿ããããªãšããããšã§ã
ãã¡ãã®ã¬ã€ãã«æ²¿ã£ãŠãèŠãŠãããŸãã
Quarkus - Testing Your Application
ç°å¢
ä»åã®ç°å¢ã¯ããã¡ãã§ãã
$ 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ãªãœãŒã¹ã¯ã©ã¹ã®ãã¹ãããã
- èšå®ãã¡ã€ã«ã®é
ç®ãèªã¿èŸŒã¿ããã€ãã¹ãæã«å€ãåãæ¿ãã
- ãã€ãã£ãã€ã¡ãŒãžã¯å¯Ÿè±¡å€ãšãã
ãªã®ã§ããã¹ãã®ã¬ã€ã以å€ã«ã次ã®ãããªã¬ã€ããåç
§ããŠæžããŠããŸãã
Quarkus - Getting started with Reactive
Quarkus - Writing JSON REST Services
Quarkus - Configuring Your Application
Quarkus - Configuration Reference Guide
ãããžã§ã¯ããäœæãã
ã§ã¯ããŸãã¯ãããžã§ã¯ããäœæããŸãã
$ 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
ãšããäžæè°ãªããã±ãŒãžããããŸããã
ãããã£ããã¡ã€ã«ãçæãããå
ã¯ããã¡ãã®ããã§ãã
https://github.com/quarkusio/quarkus/tree/1.12.1.Final/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus
äžèº«ãèŠãŸããããä»åã¯èŠããªãæ°ãããã®ã§åé€ã
$ rm -rf src/main/java/org/littlewings/resteasyjackson
ãªããåé€ãããã¡ã€ã«ã®å
ã¯ãã¡ãã«ãããŸãã
https://github.com/quarkusio/quarkus/tree/1.12.1.Final/devtools/platform-descriptor-json/src/main/resources/codestarts/quarkus/examples/resteasy-jackson-example/java/src/main/java/org/acme/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;
}
}
ãã¡ããæ°žç¶åããŠä¿æãããã®ãæã¡ãããšããã§ãããä»åã¯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ã§ã®ãã¹ãã«é¢ããã¬ã€ãã¯ããã¡ãã«ãªããŸãã
Quarkus - Testing Your Application
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>
<nativeimagepath>${project.build.directory}/${project.build.finalName}-runner</nativeimagepath>
<javautilloggingmanager>org.jboss.logmanager.LogManager</javautilloggingmanager>
<mavenhome>${maven.home}</mavenhome>
</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",
"978-4295008583",
"978-4295007753",
"978-4295008477"
)
);
AssertSubscriber<String> subscriber =
bookRepository
.findAll()
.map(Book::getIsbn)
.subscribe()
.withSubscriber(AssertSubscriber.create(10));
subscriber
.assertCompleted()
.assertItems(
"978-1492062653",
"978-4295008583",
"978-4295007753",
"978-4295008477"
);
}
@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",
"978-4295008583",
"978-4295007753",
"978-4295008477"
)
);
AssertSubscriber<String> subscriber =
bookRepository
.findAll()
.map(Book::getIsbn)
.subscribe()
.withSubscriber(AssertSubscriber.create(10));
subscriber
.assertCompleted()
.assertItems(
"978-1492062653",
"978-4295008583",
"978-4295007753",
"978-4295008477"
);
}
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ã®ãµã€ãã以åããã ãã¶é°å²æ°ãå€ãããŸãããâŠã
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
ã¢ãããŒã·ã§ã³ãä»äžããªãã£ãå Žåã¯
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åãã®ãã¹ãã©ã€ãã©ãªã§ãã
REST Assured
äœ¿ãæ¹ã¯ã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));
}
@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));
}
}
ãã¹ãã¯ã©ã¹ã«ã@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ã
https://github.com/quarkusio/quarkus/blob/1.12.1.Final/core/runtime/src/main/java/io/quarkus/runtime/configuration/ApplicationPropertiesConfigSource.java#L57
META-INF/microprofile-config.properties
ãã¡ã€ã«ãšããŠç»é²ããå Žåã240ã
https://github.com/quarkusio/quarkus/blob/1.12.1.Final/core/runtime/src/main/java/io/quarkus/runtime/configuration/ApplicationPropertiesConfigSource.java#L80
ãã¡ã€ã«ã·ã¹ãã äžã®å Žåã260ã
https://github.com/quarkusio/quarkus/blob/1.12.1.Final/core/runtime/src/main/java/io/quarkus/runtime/configuration/ApplicationPropertiesConfigSource.java#L100
ãªã®ã§ãæ°ããäœæãã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ããã¹ãã®åã«å¥ã®ãµãŒãã¹ãåããããããã¯ãã®ãã¡äœ¿ãããšã«ãªããããªããšæã£ããã
ä»åã¯ããããªãšããã§ãããŸãã