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();
}
}
Figured it out. I accidentally had @GraphQlTest(MyControllerTest.class)
instead of @GraphQlTest(MyController.class)
. It works as expected now.