javagraphqlspring-boot-3

Java GraphQLTester throwing java.lang.AssertionError: No matching errors


I have a Java 21 Spring Boot 3.2 application with GraphQL. I have several APIs, and am writing integration tests for them each. The basic flow is Controller class -> Service class -> Client class. When I try to test throwing an exception, I get the following error:

java.lang.AssertionError: No matching errors.

My basic structure is:

query getTheInfo($id: String)
{
    getTheInfo(
        id: $id
    )
}
@Controller
public class MyController {
    private final MyService service;
   
    public MyController(MyService service) {
        this.service = service;
    }

    @QueryMapping
    public String getTheInfo(@Argument String id) {
        return service.getTheInfo(id);
    }
}
@Service
public class MyService {
    private final MyClient myClient;

    public MyService(MyClient myClient) {
        this.myClient = myClient;
    }

    public String getTheInfo(String id) {
        return myClient.getTheInfo(id);
    }
}
@Component
public class MyClient {

    public String getTheInfo(String id) throws MyCustomException {
        return "the info";
    }

}

And then my test class:

import com.my.app.client.MyClient;
import com.my.app.exception.MyCustomException;
import com.my.app.service.MyService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.graphql.GraphQlTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.graphql.execution.ErrorType;
import org.springframework.graphql.test.tester.GraphQlTester;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;

@Import({MyService.class, MyClient.class})
@GraphQlTest(MyControllerTest.class)
class MyControllerTest {

    @Autowired
    private GraphQlTester graphQlTester;

    @MockBean
    private MyClient myClient;

    @Test
    void testGetTheInfo_ThrownException() {
        doThrow(new MyCustomException("myClient getTheInfo error"))
                .when(myClient).getTheInfo(anyString());

        graphQlTester.documentName(TEST_GQL_DOCUMENT_NAME)
                .variable("id", "TEST_id")
                .execute()
                .errors()
                .expect(exception -> {
                    assertEquals("[MyCustomException]: myClient getTheInfo error", exception.getMessage());
                    return true;
                });
    }

}

The error is on the .expect(exception -> { line. I have a similar test that returns a String correctly, and that test passes. But trying to test throwing an exception is not working. Here is the full error:

java.lang.AssertionError: No matching errors.
Request: document='query getTheInfo($id: String)
{
    getTheInfo(
        id: $id
    )
}
', variables={id=TEST_id}
    at org.springframework.graphql.test.tester.DefaultGraphQlTester$DefaultRequest.lambda$assertDecorator$3(DefaultGraphQlTester.java:183)
    at org.springframework.graphql.test.tester.DefaultGraphQlTester$ResponseDelegate.expectErrors(DefaultGraphQlTester.java:257)
    at org.springframework.graphql.test.tester.DefaultGraphQlTester$DefaultResponse.expect(DefaultGraphQlTester.java:315)
    at com.my.app.controller.MyControllerTest.testGetTheInfo_ThrownException(MyControllerTest.java:89)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: java.lang.AssertionError: No matching errors.
    at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:37)
    at org.springframework.test.util.AssertionErrors.assertTrue(AssertionErrors.java:70)
    at org.springframework.graphql.test.tester.DefaultGraphQlTester$ResponseDelegate.lambda$expectErrors$0(DefaultGraphQlTester.java:257)
    at org.springframework.graphql.test.tester.DefaultGraphQlTester$DefaultRequest.lambda$assertDecorator$3(DefaultGraphQlTester.java:180)
    ... 6 more

Edit: Here is my Custom GraphQL exception handler:

@Component
public class MyGraphQLExceptionHandler extends DataFetcherExceptionResolverAdapter {

    /***
     * Custom formatting for GraphQL exception handling. Without this, the response does not contain relevant information.
     *
     * @param ex the exception to resolve
     * @param env the environment for the invoked {@code DataFetcher}
     *
     * @return the formatted GraphQLError
     */
    @Override
    protected GraphQLError resolveToSingleError(@NonNull Throwable ex, @NonNull DataFetchingEnvironment env) {
        Map<String, Object> extensions = new HashMap<>();
        ErrorType errorType;

        switch (ex) {
            case NotFoundException notFoundException -> {
                errorType = ErrorType.NOT_FOUND;
                extensions.putAll(notFoundException.getExtensions());
            }

            case MyCustomException myCustomException -> {
                errorType = ErrorType.INTERNAL_ERROR;
                extensions.putAll(myCustomException.getExtensions());
            }

            case HttpClientErrorException httpException -> {
                errorType = ErrorType.BAD_REQUEST;
                extensions.put(STATUS_CODE, httpException.getStatusCode().value());
            }

            case HttpServerErrorException httpException -> {
                errorType = ErrorType.INTERNAL_ERROR;
                extensions.put(STATUS_CODE, httpException.getStatusCode().value());
            }

            default -> errorType = ErrorType.INTERNAL_ERROR;
        }

        return GraphqlErrorBuilder.newError()
                .errorType(errorType)
                .message("[" + ex.getClass().getSimpleName() + "]: " + ex.getMessage())
                .path(env.getExecutionStepInfo().getPath())
                .location(env.getField().getSourceLocation())
                .extensions(extensions)
                .build();
    }

}

Solution

  • Figured it out. I accidentally had @GraphQlTest(MyControllerTest.class) instead of @GraphQlTest(MyController.class). It works as expected now.