graphql-java/graphql-java-spring

Can not test GraphQL with MockMvc, as the response is always empty.

jaggerwang opened this issue · 2 comments

I'm using MockMvc to test GraphQL API, but tests always failure because the response is empty. The full code can be found at Spring Boot in Practice.

package net.jaggerwang.sbip.api;

...

@SpringBootTest()
@AutoConfigureMockMvc
@ActiveProfiles("test")
@Sql({"/db/init-db-test.sql"})
@Sql(scripts = {"/db/clean-db-test.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
@EnabledIfSystemProperty(named = "test.api.enabled", matches = "true")
public class UserGraphQLApiTests {
    @Autowired
    private MockMvc mvc;

    @Value("${graphql.url}")
    private String graphqlUrl;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    void login() throws Exception {
        var userEntity = UserEntity.builder().username("jaggerwang").password("123456").build();
        var content = new ObjectMapper().createObjectNode();
        content.put("query", "mutation($user: UserInput!) { authLogin(user: $user) { id username } }");
        content.putObject("variables").putObject("user").put("username", userEntity.getUsername())
                .put("password", userEntity.getPassword());
        mvc.perform(post(graphqlUrl).contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(content))).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.data.authLogin.username").value(userEntity.getUsername()));
    }

    ...
}

Test output:

...

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /graphql
       Parameters = {}
          Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"149"]
             Body = {"query":"mutation($user: UserInput!) { authLogin(user: $user) { id username } }","variables":{"user":{"username":"jaggerwang","password":"123456"}}}
    Session Attrs = {}

Handler:
             Type = graphql.spring.web.servlet.components.GraphQLController
           Method = graphql.spring.web.servlet.components.GraphQLController#graphqlPOST(String, String, String, String, String, WebRequest)

Async:
    Async started = true
     Async result = {data={authLogin={id=1, username=jaggerwang}}}

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY", Set-Cookie:"SESSION=OTFiNzRmNDgtYTIzNy00N2ZlLTk4MWQtNmMxNzMwYzU1ZDVk; Path=/; HttpOnly; SameSite=Lax"]
     Content type = null
             Body =
    Forwarded URL = null
   Redirected URL = null
          Cookies = [[MockCookie@7d9c448f name = 'SESSION', value = 'OTFiNzRmNDgtYTIzNy00N2ZlLTk4MWQtNmMxNzMwYzU1ZDVk', comment = [null], domain = [null], maxAge = -1, path = '/', secure = false, version = 0, httpOnly = true]]

...

[ERROR] login  Time elapsed: 0.187 s  <<< FAILURE!
java.lang.AssertionError: No value at JSON path "$.data.authLogin.username"
	at net.jaggerwang.sbip.api.UserGraphQLApiTests.login(UserGraphQLApiTests.java:45)
Caused by: java.lang.IllegalArgumentException: json can not be null or empty
	at net.jaggerwang.sbip.api.UserGraphQLApiTests.login(UserGraphQLApiTests.java:45)

...

Try the following code - it works for me:

  @Test
  void exampleTest() throws Exception {

    String query = new StringBuilder()
        .append("query get($id: ID!) {")
        .append(" getById (id: $id) {")
        .append("  id")
        .append("  name")
        .append(" }")
        .append("}")
        .toString();
    JSONObject variables = new JSONObject();
    variables.put("id", 1);

    MvcResult mvcResult = mockMvc.perform(post("/graphql")
        .content(generateRequest(query, variables))
        .contentType(MediaType.APPLICATION_JSON))
        .andExpect(request().asyncStarted())
        .andExpect(request().asyncResult(notNullValue()))
        .andReturn();

    mockMvc.perform(asyncDispatch(mvcResult))
        .andDo(print())
        .andExpect(status().isOk())
  }

  private String generateRequest(String query, JSONObject variables) throws JSONException {
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("query", query);
    if (variables != null) {
      jsonObject.put("variables", variables);
    }
    return jsonObject.toString();
  }

}

This project is now archived in favor of the official Spring GraphQL integration.