Skip to content
This repository was archived by the owner on Mar 28, 2020. It is now read-only.

Schema integration test #1 #37

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package lv.ctco.tpl.bff.connectors.icndb;

import lombok.Getter;
import lombok.Setter;

import java.util.List;

@Getter
@Setter
public class ICNDBJoke {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Getter and @Setter can be folded in to @Data


String id;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package lv.ctco.tpl.bff.connectors.icndb;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class ICNDBJokeEnvelope {

String type;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package lv.ctco.tpl.bff.graphql;

import lombok.SneakyThrows;
import lombok.val;
import org.json.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@RunWith(SpringRunner.class)
@AutoConfigureMockMvc
public class GraphQLIntegrationTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually an e2e test, please rename accordingly

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how can I ensure that e2e tests are not executed in the same test run as unit and integration tests?


private static final String GRAPHQL_PATH = "/graphql";

private static final String QUERY = "{jokes {jokeById(id: \"77\"){id,text}}}";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please extract to a .graphql file:
a) better IDE support, e.g. the IntelliJ GraphQL plugin will recognize and help out with the syntax
b) better readability for a human person


private static final String JOKE_ID = "77";

private static final String JOKE_TEXT = "Chuck Norris can divide by zero.";

@Autowired
private MockMvc mockMvc;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By going through MockMvc we fail to test that Spring's DispatcherServlet routes to GraphQLServlet correctly in a real world scenario i.e. when an app is deployed in to a servlet container. I believe that we should do an actual POST instead

Copy link
Contributor

@trioletas trioletas Apr 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please see this comment for a visual representation re e2e testing scope


@Test
@SneakyThrows
public void testGetJokeById() {
val result = graphQLPost();

result.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$.data.jokes.jokeById.id").value(JOKE_ID))
.andExpect(jsonPath("$.data.jokes.jokeById.text").value(JOKE_TEXT));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in case of an error, $.errors will be set to an array of errors. in order to fail fast, please assert that the error array is empty

}

@SneakyThrows
private ResultActions graphQLPost() {
return mockMvc.perform(post(GRAPHQL_PATH).content(request()));
}

@SneakyThrows
private String request() {
val jsonQuery = new JSONObject();
jsonQuery.put("query", QUERY);
return jsonQuery.toString();
}
}
88 changes: 88 additions & 0 deletions api/src/test/java/lv/ctco/tpl/bff/graphql/GraphQLQueryTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package lv.ctco.tpl.bff.graphql;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import lombok.SneakyThrows;
import lombok.val;
import lv.ctco.tpl.bff.configuration.AppProperties;
import lv.ctco.tpl.bff.connectors.icndb.ICNDB;
import lv.ctco.tpl.bff.connectors.icndb.ICNDBJoke;
import lv.ctco.tpl.bff.connectors.icndb.ICNDBJokeEnvelope;
import org.json.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;

import static org.mockito.Mockito.doReturn;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@RunWith(SpringRunner.class)
@AutoConfigureMockMvc
public class GraphQLQueryTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually an integration test, please rename accordingly


private static final String GRAPHQL_PATH = "/graphql";

private static final String QUERY = "{jokes {jokeById(id: \"77\"){id,text}}}";

private static final String JOKE_ID = "77";

private static final String JOKE_TEXT = "Chuck Norris can divide by zero.";

@Autowired
private AppProperties appProperties;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not auto wire required app properties directly? i.e.

 @Value("icndb.url") private String icndbUrl;

after all we only need a single value from the props, let's be straight about it


@Autowired
private MockMvc mockMvc;

@Mock
private ICNDB icndb;

@Test
@SneakyThrows
public void testGetJokeById() {
doReturn(mockResponse()).when(icndb).getJokeById(JOKE_ID);

val result = graphQLPost();

result.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$.data.jokes.jokeById.id").value(JOKE_ID))
.andExpect(jsonPath("$.data.jokes.jokeById.text").value(JOKE_TEXT));
}

@SneakyThrows
private ResultActions graphQLPost() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the fact that you actually do a HTTP POST is an implementation detail and is completely irrelevant to the reader, you may do a GET and nothing would change contract-wise.
please change the name to something like executeGraphQLQuery

return mockMvc.perform(post(GRAPHQL_PATH).content(request()));
}

@SneakyThrows
private String request() {
val jsonQuery = new JSONObject();
jsonQuery.put("query", QUERY);
return jsonQuery.toString();
}

private HystrixCommand<ICNDBJokeEnvelope> mockResponse() {
return new HystrixCommand<ICNDBJokeEnvelope>(HystrixCommandGroupKey.Factory.asKey(appProperties.getIcndbUrl())) {
@Override
protected ICNDBJokeEnvelope run() throws Exception {
ICNDBJokeEnvelope icndbJokeEnvelope = new ICNDBJokeEnvelope();
icndbJokeEnvelope.setType("success");
ICNDBJoke value = new ICNDBJoke();
value.setId(JOKE_ID);
value.setJoke(JOKE_TEXT);
icndbJokeEnvelope.setValue(value);
return icndbJokeEnvelope;
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package lv.ctco.tpl.bff.graphql;

import graphql.servlet.GraphQLServlet;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller
public class GraphQLTestController {

@Autowired
private GraphQLServlet graphQLServlet;

@RequestMapping(value = "${graphql.servlet.mapping:/graphql}")
@SneakyThrows
public void service(HttpServletRequest request, HttpServletResponse response) {
graphQLServlet.service(request, response);
}
}
7 changes: 6 additions & 1 deletion api/src/test/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
app:
icndb-url: https://example.com
icndb-url: https://api.icndb.com

logging:
level:
feign: DEBUG
graphql: DEBUG