CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-springframework-boot--spring-boot-starter-graphql

Starter for building GraphQL applications with Spring GraphQL

Pending
Overview
Eval results
Files

testing-support.mddocs/

Testing Support

Spring Boot GraphQL Starter provides comprehensive testing infrastructure for GraphQL applications, including test slices, mock configurations, and specialized testing utilities.

GraphQL Test Slice

The @GraphQlTest annotation provides a focused test slice for GraphQL components.

@GraphQlTest Annotation

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(GraphQlTestContextBootstrapper.class)
@ExtendWith(SpringExtension.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(GraphQlTypeExcludeFilter.class)
@AutoConfigureCache
@AutoConfigureGraphQl
@AutoConfigureGraphQlTester
@AutoConfigureJson
@ImportAutoConfiguration
public @interface GraphQlTest {
    
    @AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
    Class<?>[] excludeAutoConfiguration() default {};
    
    @AliasFor(annotation = ComponentScan.class, attribute = "useDefaultFilters")
    boolean useDefaultFilters() default true;
    
    String[] properties() default {};
}

Usage Example

@GraphQlTest(controllers = BookController.class)
class BookControllerTest {
    
    @Autowired
    private GraphQlTester graphQlTester;
    
    @MockBean
    private BookService bookService;
    
    @Test
    void shouldReturnBooks() {
        // Given
        List<Book> books = List.of(
            new Book("1", "Spring Boot in Action", "Craig Walls"),
            new Book("2", "Spring GraphQL", "Josh Long")
        );
        when(bookService.findAll()).thenReturn(books);
        
        // When & Then
        graphQlTester
            .documentName("get-books")
            .execute()
            .path("books")
            .entityList(Book.class)
            .hasSize(2)
            .contains(books.get(0), books.get(1));
    }
}

GraphQL Tester

Core testing utility for executing GraphQL operations and asserting results.

Auto-Configuration

@AutoConfiguration
@ConditionalOnClass({ GraphQlTester.class, GraphQlSource.class })
@EnableConfigurationProperties(GraphQlTesterProperties.class) 
public class GraphQlTesterAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public GraphQlTester graphQlTester(GraphQlSource graphQlSource) {
        return GraphQlTester.create(graphQlSource);
    }
}

GraphQlTester Interface

public interface GraphQlTester {
    
    // Execute with document name (from src/test/resources/graphql-test/)
    RequestSpec documentName(String name);
    
    // Execute with inline query
    RequestSpec document(String document);
    
    // Subscription testing
    SubscriptionSpec subscription(String document);
    
    interface RequestSpec {
        RequestSpec variable(String name, Object value);
        RequestSpec variables(Map<String, Object> variables);
        RequestSpec operationName(String operationName);
        RequestSpec headers(Consumer<HttpHeaders> headersConsumer);
        
        ResponseSpec execute();
    }
    
    interface ResponseSpec {
        ResponseSpec errors(Consumer<List<ResponseError>> errorsConsumer);
        PathSpec path(String path);
        EntitySpec<T> entity(Class<T> entityType);
    }
}

Testing Examples

@GraphQlTest
class GraphQlControllerTests {
    
    @Autowired
    private GraphQlTester graphQlTester;
    
    @Test
    void shouldQueryBooks() {
        graphQlTester
            .document("{ books { id title author } }")
            .execute()
            .path("books")
            .entityList(Book.class)
            .hasSize(3)
            .satisfies(books -> {
                assertThat(books).extracting(Book::getTitle)
                    .contains("Spring Boot in Action");
            });
    }
    
    @Test
    void shouldQueryBookWithVariables() {
        graphQlTester
            .documentName("book-by-id")
            .variable("id", "123")
            .execute()
            .path("book")
            .entity(Book.class)
            .satisfies(book -> {
                assertThat(book.getId()).isEqualTo("123");
                assertThat(book.getTitle()).isNotEmpty();
            });
    }
    
    @Test
    void shouldHandleErrors() {
        graphQlTester
            .document("{ nonExistentField }")
            .execute()
            .errors()
            .expect(error -> error.getErrorType() == ErrorType.ValidationError);
    }
}

HTTP GraphQL Tester

Testing GraphQL over HTTP with full web integration.

@AutoConfigureHttpGraphQlTester

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ImportAutoConfiguration
public @interface AutoConfigureHttpGraphQlTester {
    String endpoint() default "/graphql";
}

Usage Example

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureHttpGraphQlTester
class HttpGraphQlIntegrationTest {
    
    @Autowired
    private HttpGraphQlTester httpGraphQlTester;
    
    @Test
    void shouldExecuteQueryOverHttp() {
        httpGraphQlTester
            .document("{ books { id title } }")
            .execute()
            .path("books")
            .entityList(Book.class)
            .hasSize(2);
    }
    
    @Test
    void shouldExecuteWithHeaders() {
        httpGraphQlTester
            .document("{ secureBooks { id title } }")
            .headers(headers -> headers.setBearerAuth("token123"))
            .execute()
            .path("secureBooks")
            .entityList(Book.class)
            .hasSizeGreaterThan(0);
    }
}

WebSocket Testing

Testing GraphQL subscriptions over WebSocket.

WebSocket GraphQl Tester

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWebSocketGraphQlTester
class WebSocketGraphQlTest {
    
    @Autowired
    private WebSocketGraphQlTester webSocketTester;
    
    @Test
    void shouldReceiveSubscriptionUpdates() {
        Flux<Book> bookUpdates = webSocketTester
            .document("subscription { bookUpdates { id title } }")
            .executeSubscription()
            .toFlux(Book.class);
            
        StepVerifier.create(bookUpdates.take(3))
            .expectNextCount(3)
            .verifyComplete();
    }
}

Test Configuration

Test Properties

@ConfigurationProperties("spring.graphql.test")
public class GraphQlTesterProperties {
    private String endpoint = "/graphql";
    private Duration timeout = Duration.ofSeconds(5);
    private boolean prettyPrint = true;
    
    // Getters and setters
}

Test Application Properties

# application-test.yml
spring:
  graphql:
    schema:
      locations: classpath:test-schemas/
      inspection.enabled: false
    graphiql:
      enabled: true
  test:
    graphql:
      timeout: 10s
      pretty-print: true

Mock Configuration

Mocking GraphQL Components

@TestConfiguration
public class GraphQlTestConfig {
    
    @Bean
    @Primary
    public BookService mockBookService() {
        BookService mockService = Mockito.mock(BookService.class);
        
        when(mockService.findAll()).thenReturn(List.of(
            new Book("1", "Test Book", "Test Author")
        ));
        
        when(mockService.findById(anyString())).thenAnswer(invocation -> {
            String id = invocation.getArgument(0);
            return new Book(id, "Test Book " + id, "Test Author");
        });
        
        return mockService;
    }
    
    @Bean
    @Primary 
    public DataFetcherExceptionResolver mockExceptionResolver() {
        return (environment) -> {
            if (environment.getException() instanceof TestException) {
                return Mono.just(List.of(
                    GraphqlErrorBuilder.newError(environment)
                        .message("Test error")
                        .build()
                ));
            }
            return Mono.empty();
        };
    }
}

Test Data Management

Test Schema Setup

# src/test/resources/graphql-test/test-schema.graphqls
type Query {
    books: [Book!]!
    book(id: ID!): Book
}

type Mutation {
    addBook(input: BookInput!): Book!
}

type Book {
    id: ID!
    title: String!
    author: String!
}

input BookInput {
    title: String!
    author: String!
}

Test Queries

# src/test/resources/graphql-test/get-books.graphql
query GetBooks {
    books {
        id
        title
        author
    }
}
# src/test/resources/graphql-test/book-by-id.graphql
query BookById($id: ID!) {
    book(id: $id) {
        id
        title
        author
    }
}

Integration Testing

Full Application Testing

@SpringBootTest
@TestPropertySource(properties = {
    "spring.graphql.schema.locations=classpath:test-schemas/",
    "spring.graphql.graphiql.enabled=false"
})
class GraphQlApplicationTests {
    
    @Autowired
    private TestRestTemplate restTemplate;
    
    @Test
    void shouldExecuteGraphQlQuery() {
        String query = """
            {
                "query": "{ books { id title } }"
            }
            """;
            
        ResponseEntity<String> response = restTemplate.postForEntity(
            "/graphql",
            new HttpEntity<>(query, createJsonHeaders()),
            String.class
        );
        
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody()).contains("books");
    }
    
    private HttpHeaders createJsonHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        return headers;
    }
}

Performance Testing

Load Testing Setup

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class GraphQlLoadTest {
    
    @Autowired
    private HttpGraphQlTester httpTester;
    
    @Test
    void shouldHandleConcurrentRequests() {
        int numberOfThreads = 10;
        int requestsPerThread = 100;
        
        CompletableFuture<?>[] futures = IntStream.range(0, numberOfThreads)
            .mapToObj(i -> CompletableFuture.runAsync(() -> {
                for (int j = 0; j < requestsPerThread; j++) {
                    httpTester
                        .document("{ books { id title } }")
                        .execute()
                        .path("books")
                        .entityList(Book.class);
                }
            }))
            .toArray(CompletableFuture[]::new);
            
        assertThatCode(() -> CompletableFuture.allOf(futures).get(30, TimeUnit.SECONDS))
            .doesNotThrowAnyException();
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-springframework-boot--spring-boot-starter-graphql

docs

configuration-properties.md

core-infrastructure.md

data-integration.md

index.md

observability-integration.md

security-integration.md

testing-support.md

transport-support.md

web-integration.md

tile.json