diff --git a/spring-graphql-test/src/test/java/org/springframework/graphql/test/query/GraphQLTesterTests.java b/spring-graphql-test/src/test/java/org/springframework/graphql/test/query/GraphQLTesterTests.java
new file mode 100644
index 00000000..664082c7
--- /dev/null
+++ b/spring-graphql-test/src/test/java/org/springframework/graphql/test/query/GraphQLTesterTests.java
@@ -0,0 +1,402 @@
+/*
+ * 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.test.query;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import graphql.ExecutionResult;
+import graphql.ExecutionResultImpl;
+import graphql.GraphQLError;
+import graphql.GraphqlErrorBuilder;
+import graphql.language.SourceLocation;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.ArgumentCaptor;
+import reactor.core.publisher.Mono;
+
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.graphql.WebGraphQLService;
+import org.springframework.graphql.WebInput;
+import org.springframework.graphql.WebOutput;
+import org.springframework.http.HttpHeaders;
+import org.springframework.lang.Nullable;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link GraphQLTester} parameterized to:
+ *
+ * - Connect to {@link MockWebServer} and return a preset HTTP response.
+ *
- Use mock {@link WebGraphQLService} to return a preset {@link ExecutionResult}.
+ *
+ *
+ * There is no actual handling via {@link graphql.GraphQL} in either scenario.
+ * The main focus is to verify {@link GraphQLTester} request preparation and
+ * response handling.
+ */
+public class GraphQLTesterTests {
+
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+
+ public static Stream argumentSource() {
+ return Stream.of(new MockWebServerSetup(), new MockWebGraphQLServiceSetup());
+ }
+
+
+ @ParameterizedTest
+ @MethodSource("argumentSource")
+ void pathAndValueExistsAndEmptyChecks(GraphQLTesterSetup setup) throws Exception {
+
+ String query = "{me {name, friends}}";
+ setup.response("{\"me\": {\"name\":\"Luke Skywalker\", \"friends\":[]}}");
+
+ GraphQLTester.ResponseSpec spec = setup.graphQLTester().query(query).execute();
+
+ spec.path("me.name").pathExists().valueExists().valueIsNotEmpty();
+ spec.path("me.friends").valueIsEmpty();
+ spec.path("hero").pathDoesNotExist().valueDoesNotExist().valueIsEmpty();
+
+ setup.verifyRequest(input -> assertThat(input.getQuery()).contains(query));
+ setup.shutdown();
+ }
+
+ @ParameterizedTest
+ @MethodSource("argumentSource")
+ void matchesJson(GraphQLTesterSetup setup) throws Exception {
+
+ String query = "{me {name}}";
+ setup.response("{\"me\": {\"name\":\"Luke Skywalker\", \"friends\":[]}}");
+
+ GraphQLTester.ResponseSpec spec = setup.graphQLTester().query(query).execute();
+
+ spec.path("").matchesJson("{\"me\": {\"name\":\"Luke Skywalker\",\"friends\":[]}}");
+ spec.path("me").matchesJson("{\"name\":\"Luke Skywalker\"}");
+ spec.path("me").matchesJson("{\"friends\":[]}"); // lenient match with subset of fields
+
+ assertThatThrownBy(() -> spec.path("me").matchesJsonStrictly("{\"friends\":[]}"))
+ .as("Extended fields should fail in strict mode")
+ .hasMessageContaining("Unexpected: name");
+
+ setup.verifyRequest(input -> assertThat(input.getQuery()).contains(query));
+ setup.shutdown();
+ }
+
+ @ParameterizedTest
+ @MethodSource("argumentSource")
+ void entity(GraphQLTesterSetup setup) throws Exception {
+
+ String query = "{me {name}}";
+ setup.response("{\"me\": {\"name\":\"Luke Skywalker\"}}");
+
+ GraphQLTester.ResponseSpec spec = setup.graphQLTester().query(query).execute();
+
+ MovieCharacter luke = MovieCharacter.create("Luke Skywalker");
+ MovieCharacter han = MovieCharacter.create("Han Solo");
+ AtomicReference personRef = new AtomicReference<>();
+
+ MovieCharacter actual = spec.path("me").entity(MovieCharacter.class)
+ .isEqualTo(luke)
+ .isNotEqualTo(han)
+ .satisfies(personRef::set)
+ .matches(movieCharacter -> personRef.get().equals(movieCharacter))
+ .isSameAs(personRef.get())
+ .isNotSameAs(luke)
+ .get();
+
+ assertThat(actual.getName()).isEqualTo("Luke Skywalker");
+
+ spec.path("")
+ .entity(new ParameterizedTypeReference