diff --git a/spring-graphql/build.gradle b/spring-graphql/build.gradle index 28995b2c..d49b3dc5 100644 --- a/spring-graphql/build.gradle +++ b/spring-graphql/build.gradle @@ -35,6 +35,7 @@ dependencies { testImplementation 'com.querydsl:querydsl-core' testImplementation 'com.querydsl:querydsl-collections' testImplementation 'javax.servlet:javax.servlet-api' + testImplementation 'com.jayway.jsonpath:json-path' testImplementation 'com.fasterxml.jackson.core:jackson-databind' testRuntimeOnly 'org.apache.logging.log4j:log4j-core' diff --git a/spring-graphql/src/test/java/org/springframework/graphql/GraphQlResponse.java b/spring-graphql/src/test/java/org/springframework/graphql/GraphQlResponse.java new file mode 100644 index 00000000..bc5a58b3 --- /dev/null +++ b/spring-graphql/src/test/java/org/springframework/graphql/GraphQlResponse.java @@ -0,0 +1,204 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.graphql; + +import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; + +import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.TypeRef; +import com.jayway.jsonpath.spi.json.JacksonJsonProvider; +import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider; +import graphql.ExecutionResult; +import graphql.GraphQLError; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.ResolvableType; +import org.springframework.lang.Nullable; +import org.springframework.util.StringUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Wrap an {@link ExecutionResult} for testing purposes. Provide data access + * methods with JSONPath and type conversion, transparently check for no errors, + * and provide convenience methods to verify errors. + * + * @author Rossen Stoyanchev + */ +public class GraphQlResponse { + + private static final Log logger = LogFactory.getLog(GraphQlResponse.class); + + + private final DocumentContext documentContext; + + private final List errors; + + private boolean errorsChecked; + + + private GraphQlResponse(ExecutionResult result) { + this.documentContext = JsonPath.parse(result.toSpecification(), initJsonPathConfig()); + this.errors = result.getErrors(); + } + + private static Configuration initJsonPathConfig() { + return Configuration.builder() + .jsonProvider(new JacksonJsonProvider()) + .mappingProvider(new JacksonMappingProvider()) + .build(); + } + + + public GraphQlResponse log() { + logger.debug("GraphQlResponse: " + this.documentContext.jsonString()); + return this; + } + + public T toEntity(String path, Class targetClass) { + assertNoErrors(); + return this.documentContext.read(jsonPath(path), targetClass); + } + + public T toEntity(String path, ParameterizedTypeReference targetType) { + assertNoErrors(); + return this.documentContext.read(jsonPath(path), new TypeRefAdapter<>(targetType)); + } + + public List toList(String path, Class elementClass) { + assertNoErrors(); + return this.documentContext.read(jsonPath(path), new TypeRefAdapter<>(List.class, elementClass)); + } + + public List toList(String path, ParameterizedTypeReference elementType) { + assertNoErrors(); + return this.documentContext.read(jsonPath(path), new TypeRefAdapter<>(List.class, elementType)); + } + + @Nullable + public T rawValue(String path) { + assertNoErrors(); + return this.documentContext.read(jsonPath(path)); + } + + private void assertNoErrors() { + if (!this.errorsChecked) { + assertThat(this.errors).as("Errors present in GraphQL response").isEmpty(); + this.errorsChecked = true; + } + } + + private static JsonPath jsonPath(String path) { + if (!StringUtils.hasText(path)) { + path = "$.data"; + } + else if (!path.startsWith("$") && !path.startsWith("data.")) { + path = "$.data." + path; + } + return JsonPath.compile(path); + } + + public int errorCount() { + this.errorsChecked = true; + return this.errors.size(); + } + + public Error error(int index) { + this.errorsChecked = true; + return new Error(index); + } + + + public static GraphQlResponse from(ExecutionResult result) { + return new GraphQlResponse(result); + } + + public static GraphQlResponse from(Mono resultMono) { + ExecutionResult result = resultMono.block(); + assertThat(result).isNotNull(); + return from(result); + } + + public static Flux forSubscription(ExecutionResult result) { + assertThat(result.getErrors()).as("Errors present in GraphQL response").isEmpty(); + Publisher publisher = result.getData(); + return Flux.from(publisher).map(GraphQlResponse::from); + } + + @SuppressWarnings("BlockingMethodInNonBlockingContext") + public static Flux forSubscription(Mono resultMono) { + ExecutionResult result = resultMono.block(); + assertThat(result).isNotNull(); + return forSubscription(result); + } + + + public class Error { + + private final int index; + + public Error(int index) { + this.index = index; + } + + public String message() { + return GraphQlResponse.this.errors.get(index).getMessage(); + } + + public String errorType() { + return GraphQlResponse.this.errors.get(index).getErrorType().toString(); + } + + public Map extensions() { + return GraphQlResponse.this.errors.get(index).getExtensions(); + } + + } + + + private static final class TypeRefAdapter extends TypeRef { + + private final Type type; + + TypeRefAdapter(ParameterizedTypeReference typeReference) { + this.type = typeReference.getType(); + } + + TypeRefAdapter(Class clazz, Class generic) { + this.type = ResolvableType.forClassWithGenerics(clazz, generic).getType(); + } + + TypeRefAdapter(Class clazz, ParameterizedTypeReference generic) { + this.type = ResolvableType.forClassWithGenerics(clazz, ResolvableType.forType(generic)).getType(); + } + + @Override + public Type getType() { + return this.type; + } + + } + +} diff --git a/spring-graphql/src/test/java/org/springframework/graphql/GraphQlTestUtils.java b/spring-graphql/src/test/java/org/springframework/graphql/GraphQlTestUtils.java index adfe413a..20fb2375 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/GraphQlTestUtils.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/GraphQlTestUtils.java @@ -17,18 +17,13 @@ package org.springframework.graphql; import java.nio.charset.StandardCharsets; -import java.util.Map; -import graphql.ExecutionResult; import graphql.schema.DataFetcher; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.Resource; import org.springframework.graphql.execution.GraphQlSource; import org.springframework.graphql.execution.RuntimeWiringConfigurer; -import org.springframework.lang.Nullable; - -import static org.assertj.core.api.Assertions.assertThat; /** * Utility methods for GraphQL tests. @@ -36,7 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat; public abstract class GraphQlTestUtils { /** - * Convenience method for a {@link GraphQlSource.Builder} with a single {@link DataFetcher}. + * Initialize a {@link GraphQlSource.Builder} with a single {@link DataFetcher}. * * @param schema either String content or a {@link Resource}. * @param typeName the parent type name (Query, Mutation, or Subscription). @@ -53,8 +48,9 @@ public abstract class GraphQlTestUtils { } /** - * Convenience method for a {@link GraphQlSource.Builder} when multiple - * {@link DataFetcher} registrations might be needed. + * Initialize a {@link GraphQlSource.Builder} with a {@link RuntimeWiringConfigurer}, + * which may be useful for multiple {@link DataFetcher} registrations, or for a + * built-in implementation (e.g. for annotated handler methods). * * @param schema either String content or a {@link Resource}. * @param configurer the configurer to apply to the RuntimeWiring @@ -71,18 +67,4 @@ public abstract class GraphQlTestUtils { .configureRuntimeWiring(configurer); } - @SuppressWarnings("unchecked") - public static T getData(@Nullable ExecutionResult result, String key) { - Map map = getData(result); - return (T) map.get(key); - } - - public static T getData(@Nullable ExecutionResult result) { - assertThat(result).isNotNull(); - assertThat(result.getErrors()).as("Errors present in GraphQL response").isEmpty(); - T data = result.getData(); - assertThat(data).isNotNull(); - return data; - } - } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingInvocationTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingInvocationTests.java index 42343e7c..5c7fc52d 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingInvocationTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingInvocationTests.java @@ -18,6 +18,7 @@ package org.springframework.graphql.data.method.annotation.support; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -26,6 +27,8 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import graphql.ExecutionResult; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -35,6 +38,7 @@ import reactor.core.publisher.Mono; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.GraphQlService; import org.springframework.graphql.GraphQlTestUtils; import org.springframework.graphql.RequestInput; @@ -44,6 +48,7 @@ import org.springframework.graphql.execution.BatchLoaderRegistry; import org.springframework.graphql.execution.DefaultBatchLoaderRegistry; import org.springframework.graphql.execution.ExecutionGraphQlService; import org.springframework.graphql.execution.GraphQlSource; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Controller; import static org.assertj.core.api.Assertions.assertThat; @@ -55,7 +60,7 @@ import static org.junit.jupiter.params.provider.Arguments.arguments; * * @author Rossen Stoyanchev */ -@SuppressWarnings({"unchecked", "unused"}) +@SuppressWarnings("unused") public class BatchMappingInvocationTests { private static final Map courseMap = new HashMap<>(); @@ -107,30 +112,31 @@ public class BatchMappingInvocationTests { void oneToOne(Class controllerClass) { String query = "{ " + " courses { " + + " id" + " name" + " instructor {" + + " id" + " firstName" + " lastName" + " }" + " }" + "}"; - ExecutionResult result = initGraphQlService(controllerClass, CourseConfig.class) - .execute(new RequestInput(query, null, null, null)) - .block(); + Mono resultMono = graphQlService(controllerClass, CourseConfig.class) + .execute(new RequestInput(query, null, null, null)); - List> actualCourses = GraphQlTestUtils.getData(result, "courses"); + List actualCourses = GraphQlResponse.from(resultMono).toList("courses", Course.class); List courses = Course.allCourses(); assertThat(actualCourses).hasSize(courses.size()); for (int i = 0; i < courses.size(); i++) { - Map actualCourse = actualCourses.get(i); + Course actualCourse = actualCourses.get(i); Course course = courses.get(i); - assertThat(actualCourse.get("name")).isEqualTo(course.name()); + assertThat(actualCourse).isEqualTo(course); - Map actualInstructor = (Map) actualCourse.get("instructor"); - assertThat(actualInstructor.get("firstName")).isEqualTo(course.instructor().firstName()); - assertThat(actualInstructor.get("lastName")).isEqualTo(course.instructor().lastName()); + Person actualInstructor = actualCourse.instructor(); + assertThat(actualInstructor.firstName()).isEqualTo(course.instructor().firstName()); + assertThat(actualInstructor.lastName()).isEqualTo(course.instructor().lastName()); } } @@ -139,40 +145,40 @@ public class BatchMappingInvocationTests { void oneToMany(Class controllerClass) { String query = "{ " + " courses { " + + " id" + " name" + " students {" + + " id" + " firstName" + " lastName" + " }" + " }" + "}"; - ExecutionResult result = initGraphQlService(controllerClass, CourseConfig.class) - .execute(new RequestInput(query, null, null, null)) - .block(); - - List> actualCourses = GraphQlTestUtils.getData(result, "courses"); + Mono resultMono = graphQlService(controllerClass, CourseConfig.class) + .execute(new RequestInput(query, null, null, null)); + List actualCourses = GraphQlResponse.from(resultMono).toList("courses", Course.class); List courses = Course.allCourses(); assertThat(actualCourses).hasSize(courses.size()); for (int i = 0; i < courses.size(); i++) { - Map actualCourse = actualCourses.get(i); + Course actualCourse = actualCourses.get(i); Course course = courses.get(i); - assertThat(actualCourse.get("name")).isEqualTo(course.name()); + assertThat(actualCourse.name()).isEqualTo(course.name()); - List> actualStudents = (List>) actualCourse.get("students"); + List actualStudents = actualCourse.students(); List students = course.students(); assertThat(actualStudents).hasSize(students.size()); for (int j = 0; j < actualStudents.size(); j++) { - assertThat(actualStudents.get(i).get("firstName")).isEqualTo(students.get(i).firstName()); - assertThat(actualStudents.get(i).get("lastName")).isEqualTo(students.get(i).lastName()); + assertThat(actualStudents.get(i).firstName()).isEqualTo(students.get(i).firstName()); + assertThat(actualStudents.get(i).lastName()).isEqualTo(students.get(i).lastName()); } } } - private ExecutionGraphQlService initGraphQlService(Class... configClasses) { + private ExecutionGraphQlService graphQlService(Class... configClasses) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(configClasses); applicationContext.refresh(); @@ -280,6 +286,20 @@ public class BatchMappingInvocationTests { private final List studentIds; + @JsonCreator + public Course( + @JsonProperty("id") Long id, @JsonProperty("name") String name, + @JsonProperty("instructor") @Nullable Person instructor, + @JsonProperty("students") @Nullable List students) { + + this.id = id; + this.name = name; + this.instructorId = (instructor != null ? instructor.id() : -1); + this.studentIds = (students != null ? + students.stream().map(Person::id).collect(Collectors.toList()) : + Collections.emptyList()); + } + public Course(Long id, String name, Long instructorId, List studentIds) { this.id = id; this.name = name; @@ -343,7 +363,12 @@ public class BatchMappingInvocationTests { private final String lastName; - public Person(Long id, String firstName, String lastName) { + @JsonCreator + public Person( + @JsonProperty("id") Long id, + @JsonProperty("firstName") String firstName, + @JsonProperty("lastName") String lastName) { + this.id = id; this.firstName = firstName; this.lastName = lastName; diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingInvocationTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingInvocationTests.java index 41e1040d..476105fb 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingInvocationTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingInvocationTests.java @@ -16,7 +16,6 @@ package org.springframework.graphql.data.method.annotation.support; import java.util.List; -import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; @@ -25,8 +24,8 @@ import graphql.GraphQLContext; import graphql.schema.DataFetchingEnvironment; import org.dataloader.DataLoader; import org.junit.jupiter.api.Test; -import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -36,6 +35,7 @@ import org.springframework.graphql.Author; import org.springframework.graphql.Book; import org.springframework.graphql.BookCriteria; import org.springframework.graphql.BookSource; +import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.GraphQlService; import org.springframework.graphql.GraphQlTestUtils; import org.springframework.graphql.RequestInput; @@ -72,17 +72,15 @@ public class SchemaMappingInvocationTests { " }" + "}"; - ExecutionResult result = initGraphQlService() - .execute(new RequestInput(query, null, null, null)) - .block(); + Mono resultMono = graphQlService().execute(new RequestInput(query, null, null, null)); - Map book = GraphQlTestUtils.getData(result, "bookById"); - assertThat(book.get("id")).isEqualTo("1"); - assertThat(book.get("name")).isEqualTo("Nineteen Eighty-Four"); + Book book = GraphQlResponse.from(resultMono).toEntity("bookById", Book.class); + assertThat(book.getId()).isEqualTo(1); + assertThat(book.getName()).isEqualTo("Nineteen Eighty-Four"); - Map author = (Map) book.get("author"); - assertThat(author.get("firstName")).isEqualTo("George"); - assertThat(author.get("lastName")).isEqualTo("Orwell"); + Author author = book.getAuthor(); + assertThat(author.getFirstName()).isEqualTo("George"); + assertThat(author.getLastName()).isEqualTo("Orwell"); } @Test @@ -94,15 +92,12 @@ public class SchemaMappingInvocationTests { " }" + "}"; - ExecutionResult result = initGraphQlService() - .execute(new RequestInput(query, null, null, null)) - .block(); - - List> bookList = GraphQlTestUtils.getData(result, "booksByCriteria"); + Mono resultMono = graphQlService().execute(new RequestInput(query, null, null, null)); + List bookList = GraphQlResponse.from(resultMono).toList("booksByCriteria", Book.class); assertThat(bookList).hasSize(2); - assertThat(bookList.get(0).get("name")).isEqualTo("Nineteen Eighty-Four"); - assertThat(bookList.get(1).get("name")).isEqualTo("Animal Farm"); + assertThat(bookList.get(0).getName()).isEqualTo("Nineteen Eighty-Four"); + assertThat(bookList.get(1).getName()).isEqualTo("Animal Farm"); } @Test @@ -122,15 +117,12 @@ public class SchemaMappingInvocationTests { return executionInput; }); - ExecutionResult result = initGraphQlService() - .execute(requestInput) - .block(); + Mono resultMono = graphQlService().execute(requestInput); - Map author = GraphQlTestUtils.getData(result, "authorById"); - - assertThat(author.get("id")).isEqualTo("101"); - assertThat(author.get("firstName")).isEqualTo("George"); - assertThat(author.get("lastName")).isEqualTo("Orwell"); + Author author = GraphQlResponse.from(resultMono).toEntity("authorById", Author.class); + assertThat(author.getId()).isEqualTo(101); + assertThat(author.getFirstName()).isEqualTo("George"); + assertThat(author.getLastName()).isEqualTo("Orwell"); assertThat(contextRef.get().get("key")).isEqualTo("value"); } @@ -145,14 +137,13 @@ public class SchemaMappingInvocationTests { " }" + "}"; - ExecutionResult result = initGraphQlService() - .execute(new RequestInput(operation, null, null, null)) - .block(); + Mono resultMono = graphQlService() + .execute(new RequestInput(operation, null, null, null)); - Map author = GraphQlTestUtils.getData(result, "addAuthor"); - assertThat(author.get("id")).isEqualTo("99"); - assertThat(author.get("firstName")).isEqualTo("James"); - assertThat(author.get("lastName")).isEqualTo("Joyce"); + Author author = GraphQlResponse.from(resultMono).toEntity("addAuthor", Author.class); + assertThat(author.getId()).isEqualTo(99); + assertThat(author.getFirstName()).isEqualTo("James"); + assertThat(author.getLastName()).isEqualTo("Joyce"); } @Test @@ -164,31 +155,26 @@ public class SchemaMappingInvocationTests { " }" + "}"; - ExecutionResult result = initGraphQlService() - .execute(new RequestInput(operation, null, null, null)) - .block(); + Mono resultMono = graphQlService() + .execute(new RequestInput(operation, null, null, null)); - Publisher publisher = GraphQlTestUtils.getData(result); - - Flux> bookFlux = Flux.from(publisher).map(executionResult -> { - Map map = executionResult.getData(); - return (Map) map.get("bookSearch"); - }); + Flux bookFlux = GraphQlResponse.forSubscription(resultMono) + .map(response -> response.toEntity("bookSearch", Book.class)); StepVerifier.create(bookFlux) .consumeNextWith(book -> { - assertThat(book.get("id")).isEqualTo("1"); - assertThat(book.get("name")).isEqualTo("Nineteen Eighty-Four"); + assertThat(book.getId()).isEqualTo(1); + assertThat(book.getName()).isEqualTo("Nineteen Eighty-Four"); }) .consumeNextWith(book -> { - assertThat(book.get("id")).isEqualTo("5"); - assertThat(book.get("name")).isEqualTo("Animal Farm"); + assertThat(book.getId()).isEqualTo(5); + assertThat(book.getName()).isEqualTo("Animal Farm"); }) .verifyComplete(); } - private ExecutionGraphQlService initGraphQlService() { + private ExecutionGraphQlService graphQlService() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(TestConfig.class); applicationContext.refresh(); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/querydsl/QuerydslDataFetcherTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/querydsl/QuerydslDataFetcherTests.java index 8b2ff0c6..dbb4a1d7 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/querydsl/QuerydslDataFetcherTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/querydsl/QuerydslDataFetcherTests.java @@ -20,9 +20,9 @@ import java.net.URI; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.function.Consumer; +import java.util.stream.Collectors; import com.querydsl.core.types.Predicate; import graphql.schema.DataFetcher; @@ -43,6 +43,7 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.Repository; import org.springframework.graphql.Author; import org.springframework.graphql.BookSource; +import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.GraphQlTestUtils; import org.springframework.graphql.data.GraphQlRepository; import org.springframework.graphql.execution.ExecutionGraphQlService; @@ -77,10 +78,10 @@ class QuerydslDataFetcherTests { mockRepository.save(book); Consumer tester = (handler) -> { - WebOutput output = handler.handleRequest(input("{ bookById(id: 42) {name}}")).block(); + Mono outputMono = handler.handleRequest(input("{ bookById(id: 42) {name}}")); - Map map = GraphQlTestUtils.getData(output, "bookById"); - assertThat(map).hasSize(1).containsEntry("name", book.getName()); + Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class); + assertThat(actualBook.getName()).isEqualTo(book.getName()); }; // explicit wiring @@ -97,12 +98,12 @@ class QuerydslDataFetcherTests { mockRepository.saveAll(Arrays.asList(book1, book2)); Consumer tester = (handler) -> { - WebOutput output = handler.handleRequest(input("{ books {name}}")).block(); + Mono outputMono = handler.handleRequest(input("{ books {name}}")); - List> data = GraphQlTestUtils.getData(output, "books"); - assertThat(data).containsExactlyInAnyOrder( - Collections.singletonMap("name", "Breaking Bad"), - Collections.singletonMap("name", "Hitchhiker's Guide to the Galaxy")); + List names = GraphQlResponse.from(outputMono).toList("books", Book.class) + .stream().map(Book::getName).collect(Collectors.toList()); + + assertThat(names).containsExactlyInAnyOrder(book1.getName(), book2.getName()); }; // explicit wiring @@ -120,20 +121,20 @@ class QuerydslDataFetcherTests { // 1) Automatic registration only WebGraphQlHandler handler = initHandler(builder -> {}, mockRepository, null); - WebOutput output = handler.handleRequest(input("{ bookById(id: 1) {name}}")).block(); + Mono outputMono = handler.handleRequest(input("{ bookById(id: 1) {name}}")); - Map map = GraphQlTestUtils.getData(output, "bookById"); - assertThat(map).hasSize(1).containsEntry("name", "Hitchhiker's Guide to the Galaxy"); + Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class); + assertThat(actualBook.getName()).isEqualTo("Hitchhiker's Guide to the Galaxy"); // 2) Automatic registration and explicit wiring handler = initHandler( "bookById", env -> new Book(53L, "Breaking Bad", new Author(0L, "", "Heisenberg")), mockRepository); - output = handler.handleRequest(input("{ bookById(id: 1) {name}}")).block(); + outputMono = handler.handleRequest(input("{ bookById(id: 1) {name}}")); - map = GraphQlTestUtils.getData(output, "bookById"); - assertThat(map).hasSize(1).containsEntry("name", "Breaking Bad"); + actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class); + assertThat(actualBook.getName()).isEqualTo("Breaking Bad"); } @Test @@ -144,10 +145,10 @@ class QuerydslDataFetcherTests { WebGraphQlHandler handler = initHandler("bookById", QuerydslDataFetcher.builder(mockRepository).projectAs(BookProjection.class).single()); - WebOutput output = handler.handleRequest(input("{ bookById(id: 42) {name}}")).block(); + Mono outputMono = handler.handleRequest(input("{ bookById(id: 42) {name}}")); - Map map = GraphQlTestUtils.getData(output, "bookById"); - assertThat(map).hasSize(1).containsEntry("name", "Hitchhiker's Guide to the Galaxy by Douglas Adams"); + Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class); + assertThat(actualBook.getName()).isEqualTo("Hitchhiker's Guide to the Galaxy by Douglas Adams"); } @Test @@ -158,10 +159,10 @@ class QuerydslDataFetcherTests { WebGraphQlHandler handler = initHandler("bookById", QuerydslDataFetcher.builder(mockRepository).projectAs(BookDto.class).single()); - WebOutput output = handler.handleRequest(input("{ bookById(id: 42) {name}}")).block(); + Mono outputMono = handler.handleRequest(input("{ bookById(id: 42) {name}}")); - Map map = GraphQlTestUtils.getData(output, "bookById"); - assertThat(map).hasSize(1).containsEntry("name", "The book is: Hitchhiker's Guide to the Galaxy"); + Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class); + assertThat(actualBook.getName()).isEqualTo("The book is: Hitchhiker's Guide to the Galaxy"); } @Test @@ -190,10 +191,10 @@ class QuerydslDataFetcherTests { when(mockRepository.findBy(any(), any())).thenReturn(Mono.just(book)); Consumer tester = (handler) -> { - WebOutput output = handler.handleRequest(input("{ bookById(id: 1) {name}}")).block(); + Mono outputMono = handler.handleRequest(input("{ bookById(id: 1) {name}}")); - Map map = GraphQlTestUtils.getData(output, "bookById"); - assertThat(map).hasSize(1).containsEntry("name", book.getName()); + Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class); + assertThat(actualBook.getName()).isEqualTo(book.getName()); }; // explicit wiring @@ -211,12 +212,12 @@ class QuerydslDataFetcherTests { when(mockRepository.findBy(any(), any())).thenReturn(Flux.just(book1, book2)); Consumer tester = (handler) -> { - WebOutput output = handler.handleRequest(input("{ books {name}}")).block(); + Mono outputMono = handler.handleRequest(input("{ books {name}}")); - List> data = GraphQlTestUtils.getData(output, "books"); - assertThat(data).containsExactlyInAnyOrder( - Collections.singletonMap("name", "Breaking Bad"), - Collections.singletonMap("name", "Hitchhiker's Guide to the Galaxy")); + List names = GraphQlResponse.from(outputMono).toList("books", Book.class) + .stream().map(Book::getName).collect(Collectors.toList()); + + assertThat(names).containsExactlyInAnyOrder("Breaking Bad", "Hitchhiker's Guide to the Galaxy"); }; // explicit wiring diff --git a/spring-graphql/src/test/java/org/springframework/graphql/execution/BatchLoadingTests.java b/spring-graphql/src/test/java/org/springframework/graphql/execution/BatchLoadingTests.java index 4272002f..4fe3041b 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/execution/BatchLoadingTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/execution/BatchLoadingTests.java @@ -23,10 +23,12 @@ import graphql.ExecutionResult; import org.dataloader.DataLoader; import org.junit.jupiter.api.Test; import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import org.springframework.graphql.Author; import org.springframework.graphql.Book; import org.springframework.graphql.BookSource; +import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.GraphQlTestUtils; import org.springframework.graphql.RequestInput; @@ -72,18 +74,15 @@ public class BatchLoadingTests { })); }); - ExecutionResult result = service.execute(new RequestInput(query, null, null, null)).block(); + Mono resultMono = service.execute(new RequestInput(query, null, null, null)); - assertThat(result.getErrors()).isEmpty(); - Map data = result.getData(); - assertThat(data).isNotNull(); + List books = GraphQlResponse.from(resultMono).toList("booksByCriteria", Book.class); + assertThat(books).hasSize(2); - List> bookList = getValue(data, "booksByCriteria"); - assertThat(bookList).hasSize(2); - Map authorMap = (Map) bookList.get(0).get("author"); - assertThat(authorMap).isNotNull(); - assertThat(authorMap).containsEntry("firstName", "George"); - assertThat(authorMap).containsEntry("lastName", "Orwell"); + Author author = books.get(0).getAuthor(); + assertThat(author).isNotNull(); + assertThat(author.getFirstName()).isEqualTo("George"); + assertThat(author.getLastName()).isEqualTo("Orwell"); } private ExecutionGraphQlService initExecutionGraphQlService(RuntimeWiringConfigurer configurer) { @@ -93,9 +92,4 @@ public class BatchLoadingTests { return service; } - @SuppressWarnings("unchecked") - private T getValue(Map data, String key) { - return (T) data.get(key); - } - } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/execution/ClassNameTypeResolverTests.java b/spring-graphql/src/test/java/org/springframework/graphql/execution/ClassNameTypeResolverTests.java index 40e61fa2..fddbb435 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/execution/ClassNameTypeResolverTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/execution/ClassNameTypeResolverTests.java @@ -17,11 +17,13 @@ package org.springframework.graphql.execution; import java.util.Arrays; import java.util.List; -import java.util.Map; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import graphql.ExecutionResult; import org.junit.jupiter.api.Test; +import reactor.core.publisher.Mono; +import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.GraphQlTestUtils; import org.springframework.graphql.RequestInput; @@ -83,22 +85,19 @@ public class ClassNameTypeResolverTests { " }" + "}"; - ExecutionResult result = new ExecutionGraphQlService(graphQlSource) - .execute(new RequestInput(query, null, null, null)) - .block(); - - List> actualAnimals = GraphQlTestUtils.getData(result, "animals"); + Mono resultMono = new ExecutionGraphQlService(graphQlSource) + .execute(new RequestInput(query, null, null, null)); + GraphQlResponse response = GraphQlResponse.from(resultMono); for (int i = 0; i < animalList.size(); i++) { - Map actualAnimal = actualAnimals.get(i); Animal animal = animalList.get(i); - assertThat(actualAnimal.get("name")).isEqualTo(animal.getName()); - if (animal instanceof Bird) { - assertThat(actualAnimal.get("flightless")).isEqualTo(((Bird) animal).isFlightless()); + Bird bird = (Bird) response.toEntity("animals[" + i + "]", animal.getClass()); + assertThat(bird.isFlightless()).isEqualTo(((Bird) animal).isFlightless()); } else if (animal instanceof Mammal) { - assertThat(actualAnimal.get("herbivore")).isEqualTo(((Mammal) animal).isHerbivore()); + Mammal mammal = (Mammal) response.toEntity("animals[" + i + "]", animal.getClass()); + assertThat(mammal.isHerbivore()).isEqualTo(((Mammal) animal).isHerbivore()); } else { throw new IllegalStateException(); @@ -133,21 +132,19 @@ public class ClassNameTypeResolverTests { " }" + "}"; - ExecutionResult result = new ExecutionGraphQlService(graphQlSource) - .execute(new RequestInput(query, null, null, null)) - .block(); - - List> actualSightings = GraphQlTestUtils.getData(result, "sightings"); + Mono resultMono = new ExecutionGraphQlService(graphQlSource) + .execute(new RequestInput(query, null, null, null)); + GraphQlResponse response = GraphQlResponse.from(resultMono); for (int i = 0; i < animalAndPlantList.size(); i++) { - Map actualSighting = actualSightings.get(i); Object sighting = animalAndPlantList.get(i); - if (sighting instanceof Animal) { - assertThat(actualSighting.get("name")).isEqualTo(((Animal) sighting).getName()); + Animal animal = (Animal) response.toEntity("sightings[" + i + "]", sighting.getClass()); + assertThat(animal.getName()).isEqualTo(((Animal) sighting).getName()); } else if (sighting instanceof Tree) { - assertThat(actualSighting.get("family")).isEqualTo(((Tree) sighting).getFamily()); + Tree tree = (Tree) response.toEntity("sightings[" + i + "]", sighting.getClass()); + assertThat(tree.getFamily()).isEqualTo(((Tree) sighting).getFamily()); } else { throw new IllegalStateException(); @@ -227,6 +224,7 @@ public class ClassNameTypeResolverTests { } + @JsonIgnoreProperties(ignoreUnknown = true) static class Penguin extends BaseBird { Penguin() { @@ -236,6 +234,7 @@ public class ClassNameTypeResolverTests { } + @JsonIgnoreProperties(ignoreUnknown = true) static class Dog extends BaseMammal { Dog() { @@ -245,6 +244,7 @@ public class ClassNameTypeResolverTests { } + @JsonIgnoreProperties(ignoreUnknown = true) static class GrayWolf extends BaseMammal { GrayWolf() { @@ -268,6 +268,7 @@ public class ClassNameTypeResolverTests { } + @JsonIgnoreProperties(ignoreUnknown = true) static class GiantRedwood extends Tree { GiantRedwood() { diff --git a/spring-graphql/src/test/java/org/springframework/graphql/execution/ContextDataFetcherDecoratorTests.java b/spring-graphql/src/test/java/org/springframework/graphql/execution/ContextDataFetcherDecoratorTests.java index 334f3298..241a88f3 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/execution/ContextDataFetcherDecoratorTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/execution/ContextDataFetcherDecoratorTests.java @@ -18,19 +18,19 @@ package org.springframework.graphql.execution; import java.time.Duration; import java.util.List; -import java.util.Map; import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.GraphQL; import graphql.schema.DataFetcher; import org.junit.jupiter.api.Test; -import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; import reactor.util.context.Context; import reactor.util.context.ContextView; +import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.GraphQlTestUtils; import org.springframework.graphql.TestThreadLocalAccessor; @@ -54,9 +54,10 @@ public class ContextDataFetcherDecoratorTests { ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greeting }").build(); ReactorContextManager.setReactorContext(Context.of("name", "007"), input); - Map data = graphQl.executeAsync(input).get().getData(); + ExecutionResult executionResult = graphQl.executeAsync(input).get(); - assertThat(data).hasSize(1).containsEntry("greeting", "Hello 007"); + String greeting = GraphQlResponse.from(executionResult).toEntity("greeting", String.class); + assertThat(greeting).isEqualTo("Hello 007"); } @Test @@ -75,7 +76,7 @@ public class ContextDataFetcherDecoratorTests { ExecutionResult result = graphQl.executeAsync(input).get(); - List data = GraphQlTestUtils.getData(result, "greetings"); + List data = GraphQlResponse.from(result).toList("greetings", String.class);; assertThat(data).containsExactly("Hi 007", "Bonjour 007", "Hola 007"); } @@ -92,14 +93,14 @@ public class ContextDataFetcherDecoratorTests { ExecutionInput input = ExecutionInput.newExecutionInput().query("subscription { greetings }").build(); ReactorContextManager.setReactorContext(Context.of("name", "007"), input); - Publisher publisher = graphQl.executeAsync(input).get().getData(); + ExecutionResult executionResult = graphQl.executeAsync(input).get(); - List actual = Flux.from(publisher).cast(ExecutionResult.class) - .map((result) -> GraphQlTestUtils.getData(result, "greetings")) - .collectList() - .block(); + Flux greetingsFlux = GraphQlResponse.forSubscription(executionResult) + .map(response -> response.toEntity("greetings", String.class)); - assertThat(actual).containsExactly("Hi 007", "Bonjour 007", "Hola 007"); + StepVerifier.create(greetingsFlux) + .expectNext("Hi 007", "Bonjour 007", "Hola 007") + .verifyComplete(); } @Test @@ -116,12 +117,11 @@ public class ContextDataFetcherDecoratorTests { ContextView view = ReactorContextManager.extractThreadLocalValues(accessor, Context.empty()); ReactorContextManager.setReactorContext(view, input); - ExecutionResult result = Mono.delay(Duration.ofMillis(10)) - .flatMap((aLong) -> Mono.fromFuture(graphQl.executeAsync(input))) - .block(); + Mono resultMono = Mono.delay(Duration.ofMillis(10)) + .flatMap((aLong) -> Mono.fromFuture(graphQl.executeAsync(input))); - Map data = GraphQlTestUtils.getData(result); - assertThat(data).hasSize(1).containsEntry("greeting", "Hello 007"); + String greeting = GraphQlResponse.from(resultMono).toEntity("greeting", String.class); + assertThat(greeting).isEqualTo("Hello 007"); } finally { nameThreadLocal.remove(); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandlerTests.java b/spring-graphql/src/test/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandlerTests.java index 095294b2..e1e34632 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandlerTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandlerTests.java @@ -18,8 +18,6 @@ package org.springframework.graphql.execution; import java.time.Duration; import java.util.Collections; -import java.util.List; -import java.util.Map; import java.util.function.BiFunction; import graphql.ExecutionInput; @@ -33,6 +31,7 @@ import reactor.core.publisher.Mono; import reactor.util.context.Context; import reactor.util.context.ContextView; +import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.GraphQlTestUtils; import org.springframework.graphql.TestThreadLocalAccessor; @@ -55,13 +54,13 @@ public class ExceptionResolversExceptionHandlerTests { ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greeting }").build(); ExecutionResult result = graphQl.executeAsync(input).get(); - Map data = result.getData(); - assertThat(data).hasSize(1).containsEntry("greeting", null); + GraphQlResponse response = GraphQlResponse.from(result); + assertThat(response.errorCount()).isEqualTo(1); + assertThat(response.error(0).message()).isEqualTo("Resolved error: Invalid greeting"); + assertThat(response.error(0).errorType()).isEqualTo("BAD_REQUEST"); - List errors = result.getErrors(); - assertThat(errors).hasSize(1); - assertThat(errors.get(0).getMessage()).isEqualTo("Resolved error: Invalid greeting"); - assertThat(errors.get(0).getErrorType().toString()).isEqualTo("BAD_REQUEST"); + String greeting = response.rawValue("greeting"); + assertThat(greeting).isNull(); } @Test @@ -77,8 +76,9 @@ public class ExceptionResolversExceptionHandlerTests { ExecutionResult result = graphQl.executeAsync(input).get(); - List errors = result.getErrors(); - assertThat(errors.get(0).getMessage()).isEqualTo("Resolved error: Invalid greeting, name=007"); + GraphQlResponse response = GraphQlResponse.from(result); + assertThat(response.errorCount()).isEqualTo(1); + assertThat(response.error(0).message()).isEqualTo("Resolved error: Invalid greeting, name=007"); } @Test @@ -97,12 +97,12 @@ public class ExceptionResolversExceptionHandlerTests { ContextView view = ReactorContextManager.extractThreadLocalValues(accessor, Context.empty()); ReactorContextManager.setReactorContext(view, input); - ExecutionResult result = Mono.delay(Duration.ofMillis(10)) - .flatMap((aLong) -> Mono.fromFuture(graphQl.executeAsync(input))) - .block(); + Mono result = Mono.delay(Duration.ofMillis(10)) + .flatMap((aLong) -> Mono.fromFuture(graphQl.executeAsync(input))); - List errors = result.getErrors(); - assertThat(errors.get(0).getMessage()).isEqualTo("Resolved error: Invalid greeting, name=007"); + GraphQlResponse response = GraphQlResponse.from(result); + assertThat(response.errorCount()).isEqualTo(1); + assertThat(response.error(0).message()).isEqualTo("Resolved error: Invalid greeting, name=007"); } finally { nameThreadLocal.remove(); @@ -116,14 +116,13 @@ public class ExceptionResolversExceptionHandlerTests { ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greeting }").build(); ExecutionResult result = graphQl.executeAsync(input).get(); - Map data = result.getData(); - assertThat(data).hasSize(1).containsEntry("greeting", null); + GraphQlResponse response = GraphQlResponse.from(result); + assertThat(response.errorCount()).isEqualTo(1); + assertThat(response.error(0).message()).isEqualTo("Invalid greeting"); + assertThat(response.error(0).errorType()).isEqualTo("INTERNAL_ERROR"); - List errors = result.getErrors(); - assertThat(errors).hasSize(1); - GraphQLError error = errors.get(0); - assertThat(error.getMessage()).isEqualTo("Invalid greeting"); - assertThat(error.getErrorType().toString()).isEqualTo("INTERNAL_ERROR"); + String greeting = response.rawValue("greeting"); + assertThat(greeting).isNull(); } @Test @@ -133,9 +132,8 @@ public class ExceptionResolversExceptionHandlerTests { ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greeting }").build(); ExecutionResult result = graphQl.executeAsync(input).get(); - Map data = result.getData(); - assertThat(data).hasSize(1).containsEntry("greeting", null); - assertThat(result.getErrors()).hasSize(0); + String greeting = GraphQlResponse.from(result).rawValue("greeting"); + assertThat(greeting).isNull(); } private static GraphQL initGraphQl(DataFetcherExceptionResolver exceptionResolver) { diff --git a/spring-graphql/src/test/java/org/springframework/graphql/web/ConsumeOneAndNeverCompleteInterceptor.java b/spring-graphql/src/test/java/org/springframework/graphql/web/ConsumeOneAndNeverCompleteInterceptor.java index 223d6e4c..5f99b915 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/web/ConsumeOneAndNeverCompleteInterceptor.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/web/ConsumeOneAndNeverCompleteInterceptor.java @@ -20,16 +20,15 @@ import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import static org.assertj.core.api.Assertions.assertThat; - public class ConsumeOneAndNeverCompleteInterceptor implements WebInterceptor { @Override public Mono intercept(WebInput webInput, WebInterceptorChain chain) { - return chain.next(webInput).map((output) -> output.transform((builder) -> { - Publisher publisher = output.getData(); - assertThat(publisher).isNotNull(); - builder.data(Flux.from(publisher).take(1).concatWith(Flux.never())); + return chain.next(webInput).map((output) -> output.transform(builder -> { + if (output.getData() instanceof Publisher) { + Flux flux = Flux.from((Publisher) output.getData()).take(1).concatWith(Flux.never()); + builder.data(flux); + } })); } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/web/WebGraphQlHandlerTests.java b/spring-graphql/src/test/java/org/springframework/graphql/web/WebGraphQlHandlerTests.java index 94104f03..bff34dc2 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/web/WebGraphQlHandlerTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/web/WebGraphQlHandlerTests.java @@ -19,8 +19,6 @@ package org.springframework.graphql.web; import java.net.URI; import java.time.Duration; import java.util.Collections; -import java.util.List; -import java.util.Map; import java.util.function.BiFunction; import graphql.GraphQLError; @@ -29,6 +27,7 @@ import graphql.schema.DataFetchingEnvironment; import org.junit.jupiter.api.Test; import reactor.core.publisher.Mono; +import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.GraphQlService; import org.springframework.graphql.GraphQlTestUtils; import org.springframework.graphql.TestThreadLocalAccessor; @@ -47,8 +46,7 @@ import static org.assertj.core.api.Assertions.assertThat; public class WebGraphQlHandlerTests { private static final WebInput webInput = new WebInput( - URI.create("http://abc.org"), new HttpHeaders(), Collections.singletonMap("query", "{ greeting }"), - null, "1"); + URI.create("https://abc.org"), new HttpHeaders(), Collections.singletonMap("query", "{ greeting }"), null, "1"); @Test void reactorContextPropagation() { @@ -62,11 +60,11 @@ public class WebGraphQlHandlerTests { GraphQlService service = new ExecutionGraphQlService(graphQlSource); WebGraphQlHandler handler = WebGraphQlHandler.builder(service).build(); - WebOutput webOutput = handler.handleRequest(webInput) - .contextWrite((context) -> context.put("name", "007")).block(); + Mono outputMono = handler.handleRequest(webInput) + .contextWrite((context) -> context.put("name", "007")); - Map data = webOutput.getData(); - assertThat(data).hasSize(1).containsEntry("greeting", "Hello 007"); + String greeting = GraphQlResponse.from(outputMono).toEntity("greeting", String.class); + assertThat(greeting).isEqualTo("Hello 007"); } @Test @@ -88,15 +86,15 @@ public class WebGraphQlHandlerTests { GraphQlService service = new ExecutionGraphQlService(graphQlSource); WebGraphQlHandler handler = WebGraphQlHandler.builder(service).build(); - WebOutput webOutput = handler.handleRequest(webInput) - .contextWrite((context) -> context.put("name", "007")).block(); + Mono outputMono = handler.handleRequest(webInput) + .contextWrite((context) -> context.put("name", "007")); - Map data = webOutput.getData(); - assertThat(data).hasSize(1).containsEntry("greeting", null); + GraphQlResponse response = GraphQlResponse.from(outputMono); + assertThat(response.errorCount()).isEqualTo(1); + assertThat(response.error(0).message()).isEqualTo("Resolved error: Invalid greeting, name=007"); - List errors = webOutput.getErrors(); - assertThat(errors).hasSize(1); - assertThat(errors.get(0).getMessage()).isEqualTo("Resolved error: Invalid greeting, name=007"); + String greeting = response.rawValue("greeting"); + assertThat(greeting).isNull(); } @Test @@ -117,9 +115,10 @@ public class WebGraphQlHandlerTests { .threadLocalAccessor(threadLocalAccessor) .build(); - Map data = handler.handleRequest(webInput).block().getData(); + Mono outputMono = handler.handleRequest(webInput); - assertThat(data).hasSize(1).containsEntry("greeting", "Hello 007"); + String greeting = GraphQlResponse.from(outputMono).toEntity("greeting", String.class); + assertThat(greeting).isEqualTo("Hello 007"); } finally { nameThreadLocal.remove(); @@ -152,10 +151,11 @@ public class WebGraphQlHandlerTests { .threadLocalAccessor(threadLocalAccessor) .build(); - WebOutput webOutput = handler.handleRequest(webInput).block(); + Mono outputMono = handler.handleRequest(webInput); - List errors = webOutput.getErrors(); - assertThat(errors.get(0).getMessage()).isEqualTo("Resolved error: Invalid greeting, name=007"); + GraphQlResponse response = GraphQlResponse.from(outputMono); + assertThat(response.errorCount()).isEqualTo(1); + assertThat(response.error(0).message()).isEqualTo("Resolved error: Invalid greeting, name=007"); } finally { nameThreadLocal.remove();