Consider DataFetcherExceptionResolver in GraphQTest

Prior to this commit, `@GraphQlTest` slice tests would only consider
JsonComponent, RuntimeWiringConfigurer, Converter and
GenericConverter beans. But DataFetcherExceptionResolver,
Instrumentation and GraphQlSourceBuilderCustomizer are used in
setting up the GraphQL source.

This commit ensures that the `GraphQlTypeExcludeFilter` considers
those bean types.

Closes gh-30078
This commit is contained in:
Gautham Mohan 2022-03-07 11:55:48 +05:30 committed by Brian Clozel
parent 087e853c5d
commit 4388910435
4 changed files with 107 additions and 2 deletions

View File

@ -444,7 +444,7 @@ There are `GraphQlTester` variants and Spring Boot will auto-configure them depe
Spring Boot helps you to test your {spring-graphql-docs}#controllers[Spring GraphQL Controllers] with the `@GraphQlTest` annotation.
`@GraphQlTest` auto-configures the Spring GraphQL infrastructure, without any transport nor server being involved.
This limits scanned beans to `@Controller`, `RuntimeWiringConfigurer`, `JsonComponent`, `Converter` and `GenericConverter`.
This limits scanned beans to `@Controller`, `RuntimeWiringConfigurer`, `JsonComponent`, `Converter`, `GenericConverter`, `DataFetcherExceptionResolver`, `Instrumentation` and `GraphQlSourceBuilderCustomizer`.
Regular `@Component` and `@ConfigurationProperties` beans are not scanned when the `@GraphQlTest` annotation is used.
`@EnableConfigurationProperties` can be used to include `@ConfigurationProperties` beans.

View File

@ -51,6 +51,9 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
* <li>{@code @JsonComponent}
* <li>{@code Converter}
* <li>{@code GenericConverter}
* <li>{@code DataFetcherExceptionResolver}
* <li>{@code Instrumentation}
* <li>{@code GraphQlSourceBuilderCustomizer}
* </ul>
* <p>
* The annotation does not automatically load {@code @Component}, {@code @Service},
@ -121,7 +124,9 @@ public @interface GraphQlTest {
* {@link SpringBootApplication @SpringBootApplication}. By default, only
* {@code @Controller} (when no explicit {@link #controllers() controllers} are
* defined), {@code RuntimeWiringConfigurer}, {@code @JsonComponent},
* {@code Converter}, and {@code GenericConverter} beans are included.
* {@code Converter}, {@code GenericConverter}, {@code DataFetcherExceptionResolver},
* {@code Instrumentation} and {@code GraphQlSourceBuilderCustomizer} beans are
* included.
* @see #includeFilters()
* @see #excludeFilters()
* @return if default filters should be used

View File

@ -21,11 +21,15 @@ import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import graphql.execution.instrumentation.Instrumentation;
import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.jackson.JsonComponent;
import org.springframework.boot.test.autoconfigure.filter.StandardAnnotationCustomizableTypeExcludeFilter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.graphql.execution.DataFetcherExceptionResolver;
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
import org.springframework.stereotype.Controller;
import org.springframework.util.ClassUtils;
@ -51,6 +55,9 @@ public class GraphQlTypeExcludeFilter extends StandardAnnotationCustomizableType
includes.add(RuntimeWiringConfigurer.class);
includes.add(Converter.class);
includes.add(GenericConverter.class);
includes.add(DataFetcherExceptionResolver.class);
includes.add(Instrumentation.class);
includes.add(GraphQlSourceBuilderCustomizer.class);
for (String optionalInclude : OPTIONAL_INCLUDES) {
try {
includes.add(ClassUtils.forName(optionalInclude, null));

View File

@ -17,17 +17,35 @@
package org.springframework.boot.test.autoconfigure.graphql;
import java.io.IOException;
import java.util.List;
import com.fasterxml.jackson.databind.module.SimpleModule;
import graphql.ExecutionResult;
import graphql.GraphQLError;
import graphql.execution.instrumentation.ExecutionStrategyInstrumentationContext;
import graphql.execution.instrumentation.Instrumentation;
import graphql.execution.instrumentation.InstrumentationContext;
import graphql.execution.instrumentation.parameters.InstrumentationExecuteOperationParameters;
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
import graphql.execution.instrumentation.parameters.InstrumentationExecutionStrategyParameters;
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
import graphql.execution.instrumentation.parameters.InstrumentationFieldParameters;
import graphql.execution.instrumentation.parameters.InstrumentationValidationParameters;
import graphql.language.Document;
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.idl.RuntimeWiring;
import graphql.validation.ValidationError;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.graphql.execution.DataFetcherExceptionResolver;
import org.springframework.graphql.execution.GraphQlSource.Builder;
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
import org.springframework.graphql.web.WebInput;
import org.springframework.graphql.web.WebInterceptor;
@ -58,6 +76,9 @@ class GraphQlTypeExcludeFilterTests {
assertThat(excludes(filter, ExampleRepository.class)).isTrue();
assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue();
assertThat(excludes(filter, ExampleModule.class)).isFalse();
assertThat(excludes(filter, ExampleDataFetcherExceptionResolver.class)).isFalse();
assertThat(excludes(filter, ExampleInstrumentation.class)).isFalse();
assertThat(excludes(filter, ExampleGraphQlSourceBuilderCustomizer.class)).isFalse();
}
@Test
@ -70,6 +91,9 @@ class GraphQlTypeExcludeFilterTests {
assertThat(excludes(filter, ExampleRepository.class)).isTrue();
assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue();
assertThat(excludes(filter, ExampleModule.class)).isFalse();
assertThat(excludes(filter, ExampleDataFetcherExceptionResolver.class)).isFalse();
assertThat(excludes(filter, ExampleInstrumentation.class)).isFalse();
assertThat(excludes(filter, ExampleGraphQlSourceBuilderCustomizer.class)).isFalse();
}
@Test
@ -82,6 +106,9 @@ class GraphQlTypeExcludeFilterTests {
assertThat(excludes(filter, ExampleRepository.class)).isTrue();
assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue();
assertThat(excludes(filter, ExampleModule.class)).isTrue();
assertThat(excludes(filter, ExampleDataFetcherExceptionResolver.class)).isTrue();
assertThat(excludes(filter, ExampleInstrumentation.class)).isTrue();
assertThat(excludes(filter, ExampleGraphQlSourceBuilderCustomizer.class)).isTrue();
}
@Test
@ -94,6 +121,9 @@ class GraphQlTypeExcludeFilterTests {
assertThat(excludes(filter, ExampleRepository.class)).isFalse();
assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue();
assertThat(excludes(filter, ExampleModule.class)).isFalse();
assertThat(excludes(filter, ExampleDataFetcherExceptionResolver.class)).isFalse();
assertThat(excludes(filter, ExampleInstrumentation.class)).isFalse();
assertThat(excludes(filter, ExampleGraphQlSourceBuilderCustomizer.class)).isFalse();
}
@Test
@ -106,6 +136,9 @@ class GraphQlTypeExcludeFilterTests {
assertThat(excludes(filter, ExampleRepository.class)).isTrue();
assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue();
assertThat(excludes(filter, ExampleModule.class)).isFalse();
assertThat(excludes(filter, ExampleDataFetcherExceptionResolver.class)).isFalse();
assertThat(excludes(filter, ExampleInstrumentation.class)).isFalse();
assertThat(excludes(filter, ExampleGraphQlSourceBuilderCustomizer.class)).isFalse();
}
private boolean excludes(GraphQlTypeExcludeFilter filter, Class<?> type) throws IOException {
@ -181,4 +214,64 @@ class GraphQlTypeExcludeFilterTests {
}
static class ExampleDataFetcherExceptionResolver implements DataFetcherExceptionResolver {
@Override
public Mono<List<GraphQLError>> resolveException(Throwable exception, DataFetchingEnvironment environment) {
return null;
}
}
static class ExampleInstrumentation implements Instrumentation {
@Override
public InstrumentationContext<ExecutionResult> beginExecution(InstrumentationExecutionParameters parameters) {
return null;
}
@Override
public InstrumentationContext<Document> beginParse(InstrumentationExecutionParameters parameters) {
return null;
}
@Override
public InstrumentationContext<List<ValidationError>> beginValidation(
InstrumentationValidationParameters parameters) {
return null;
}
@Override
public InstrumentationContext<ExecutionResult> beginExecuteOperation(
InstrumentationExecuteOperationParameters parameters) {
return null;
}
@Override
public ExecutionStrategyInstrumentationContext beginExecutionStrategy(
InstrumentationExecutionStrategyParameters parameters) {
return null;
}
@Override
public InstrumentationContext<ExecutionResult> beginField(InstrumentationFieldParameters parameters) {
return null;
}
@Override
public InstrumentationContext<Object> beginFieldFetch(InstrumentationFieldFetchParameters parameters) {
return null;
}
}
static class ExampleGraphQlSourceBuilderCustomizer implements GraphQlSourceBuilderCustomizer {
@Override
public void customize(Builder builder) {
}
}
}