Good morning,
I'm trying to test some POST requests on my controllers.
I have no problems with GET request :
#Test
public void testGetAll() {
TestModel test = new TestModel();
test.done = true;
test.name = "Pierre";
test.save();
TestModel test2 = new TestModel();
test2.done = true;
test2.name = "Paul";
test2.save();
Result result = new controllers.ressources.TestRessource().get(null);
assertEquals(200, result.status());
assertEquals("text/plain", result.contentType());
assertEquals("utf-8", result.charset());
assertTrue(contentAsString(result).contains("Pierre"));
assertTrue(contentAsString(result).contains("Paul"));
}
But when I have to test a POST request, i can't give POST params to the controller.
here is the method I want to test :
public Result post() {
Map<String, String> params = RequestUtils.convertRequestForJsonDecode(request().queryString());
T model = Json.fromJson(Json.toJson(params), genericType);
model.save();
reponse.setData(model);
return ok(Json.prettyPrint(Json.toJson(reponse)));
}
I've try several solutions, but I can't find a proper one :
Try to use FakeRequest
Try to Mock the Http.Request object
So, what is the best way to write tests for my controllers ?
I'm using Play Framework 2.4.6 with Java.
Junit 4 and Mockito.
For tests of an POST action I use the RequestBuilder and the play.test.Helpers.route method.
For one with JSON data it could look like this (I use Jackson's ObjectMapper for marshaling):
public class MyTests {
protected Application application;
#Before
public void startApp() throws Exception {
ClassLoader classLoader = FakeApplication.class.getClassLoader();
application = new GuiceApplicationBuilder().in(classLoader)
.in(Mode.TEST).build();
Helpers.start(application);
}
#Test
public void myPostActionTest() throws Exception {
JsonNode jsonNode = (new ObjectMapper()).readTree("{ \"someName\": \"sameValue\" }");
RequestBuilder request = new RequestBuilder().method("POST")
.bodyJson(jsonNode)
.uri(controllers.routes.MyController.myAction().url());
Result result = route(request);
assertThat(result.status()).isEqualTo(OK);
}
#After
public void stopApp() throws Exception {
Helpers.stop(application);
}
}
Related
Note client is RestHightLevelClient,
#Override
public void createAlias(final String aliasName, final String indexName, final boolean writable)
throws IOException {
IndicesAliasesRequest request = new IndicesAliasesRequest();
AliasActions aliasAction = new AliasActions(AliasActions.Type.ADD).index(indexName)
.alias(aliasName);
if (writable) {
aliasAction.writeIndex(true);
}
request.addAliasAction(aliasAction);
AcknowledgedResponse response = client.indices().updateAliases(request, RequestOptions.DEFAULT);
}
I tried writing test case for this :
#Test
void testCreateAlias() throws IOException {
AcknowledgedResponse response = AcknowledgedResponse.of(true);
when(client.indices().updateAliases(Mockito.mock(IndicesAliasesRequest.class), RequestOptions.DEFAULT))
.thenReturn(response);
searchManagerService.createAlias("test", "test_idx", true);
}
ERROR : client.indices() is null,
How to resolve this ?
Mock client.indices() to return a mocked instance of IndicesClient. Then mock that IndicesClient updateAliases method to return response.
var mockedIndicesClient = Mockito.mock(IndicesClient.class);
when(client.indices()).thenReturn(mockedIndicesClient);
when(mockedIndicesClient.updateAliases(Mockito.mock(IndicesAliasesRequest.class), RequestOptions.DEFAULT)).thenReturn(response);
Also I believe you want to use matchers in the last line. any(IndicesAliasesRequest.class) instead of Mockito.mock:
when(mockedIndicesClient.updateAliases(any(IndicesAliasesRequest.class), RequestOptions.DEFAULT)).thenReturn(response);
I am trying to cover below main class if (!Utility.isEmpty(errors)) block code coverage and i tried below code for adding errors in array List inside client class but still array showing as empty can some one help me to solve this issue.
Main class
public void processDiohShippingMethod(ShippingMethodRequest request) {
List<ResolvableErrorEnum> errors = new ArrayList<>();
checkoutRestClient.updateShippingMethod(request, errors);
if (!Utility.isEmpty(errors)) {
logger.info("ProcessDiohShippingMethod: Shipping method update failure from {} -> {}. Reverting. ",
existingSm, request.getShippingMethodType());
if (existingShippingMethod != null && Constants.RTG_ENJOY.equalsIgnoreCase(existingSm)) {
request.setStartTime(existingShippingMethod.getDropOffShippingInfo().getStartTime());
request.setEndTime(existingShippingMethod.getDropOffShippingInfo().getEndTime());
request.setScheduleType(existingShippingMethod.getDropOffShippingInfo().getCustomerScheduleType());
}
}
}
Client
public void updateShippingMethod(ShippingMethodRequest request, List<ResolvableErrorEnum> errors) {
String url = Utils.formatHttpUrl(url, Cart_Request);
try {
HttpEntity<?> entity = new HttpEntity<>(request);
JsonNode jsonNode = restTemplate.postForObject(url, entity, JsonNode.class);
if (!jsonNode.has(Constants.CONTENT)
|| !jsonNode.path(Constants.CONTENT).path(Constants.ERRORS).isMissingNode()) {
errors.add(ErrorMessages.SM_ERR_01);
return;
}
} catch (HttpClientErrorException e) {
errors.add(ErrorMessages.SM_ERR_01);
} catch (HttpServerErrorException e) {
errors.add(ErrorMessages.SM_ERR_01);
}
}
Test
#Test
public void test_processMethod() throws Exception {
Request req = new Request();
HttpEntity<?> entity = new HttpEntity<>(req);
String jsonResponse = "{\"error\":{\"errorId\":\"1234\",\"message\":\"error message\"}},\"status\":\"success\"}}";
ObjectMapper mapper = new ObjectMapper();
JsonNode actualObj = mapper.readTree(jsonResponse);
when(restTemplate.postForObject("url", entity, JsonNode.class)).thenReturn(actualObj);
ShippingMethodResponse response = helper.processDiohShippingMethod(req);
}
Since you didn't post the whole test code this is just an educated guess, but the most probable cause is that your helper object (the one from the test method) uses a different instance, but not the one you mocked (since I can't see any code in the test, that would set the client).
So a solution could look like this:
#Test
public void test_processMethod() throws Exception {
Request req = new Request();
HttpEntity<?> entity = new HttpEntity<>(req);
String jsonResponse = "{\"error\":{\"errorId\":\"1234\",\"message\":\"error message\"}},\"status\":\"success\"}}";
ObjectMapper mapper = new ObjectMapper();
JsonNode actualObj = mapper.readTree(jsonResponse);
when(restTemplate.postForObject("url", entity, JsonNode.class)).thenReturn(actualObj);
//get this mocked object into the helper object somehow
//otherwhise the mocked method will never be called
helper.setClient(restTemplate);
ShippingMethodResponse response = helper.processDiohShippingMethod(req);
}
You probably assumed the mocking call when(restTemplate.postForObject(...))... to mock the method for all objects of this class. But it will just create a decorator- / wrapper-object arround the object. So you've got to make shure that you call the mocked method on the mocked object, but not on any other object.
When I create a case where exception is thrown by the code by calling APIs, at that time, ExceptionHandler is invoked as expected. But when I try creating the same case through unit tests, at that time, ExceptionHandler is not invoked. My classes are as below:
Controller.java
#Post("/XXX")
public ResponseEntity<List<CategoryTopicBean>> listCategoryTopics(#Body CategoryIdsRequestBean categoryIdsRequestBean) {
if (categoryIdsRequestBean.getCategoryIds().size() > MAX_ALLOWED_CATEGORY_SELECTION) {
throw new CustomException(SystemConstants.ResponseCode.ERROR, SystemConstants.ResponseMessage.OVERFLOW_MAX_CATEGORIES);
}
...
CustomExceptionHandler.java:
#Produces
#Singleton
#Requires(classes = {CustomException.class, ExceptionHandler.class})
public class CustomExceptionHandler implements ExceptionHandler<CustomException, HttpResponse> {
#Override
public HttpResponse handle(HttpRequest request, CustomException exception) {
return HttpResponse.ok(new ResponseEntity<>(exception.responseCode, exception.getMessage()));
}
}
XXXShould.java
#Test
public void should_list_category_topics() {
CategoryIdsRequestBean categoryIdsBean = new CategoryIdsRequestBean();
IdBean idBean = new IdBean();
idBean.setId(ID_1);
categoryIdsBean.setCategoryIds(Arrays.asList(idBean));
ResponseEntity<List<CategoryTopicBean>> responseEntity = topicController.listCategoryTopics(categoryIdsBean);
assertThat(SystemConstants.ResponseCode.SUCCESS).isEqualTo(responseEntity.getResponseCode());
assertThat(1).isEqualTo(responseEntity.getData().size());
categoryIdsBean = new CategoryIdsRequestBean();
categoryIdsBean.setCategoryIds(Arrays.asList(idBean, idBean, idBean, idBean, idBean, idBean));
responseEntity = topicController.listCategoryTopics(categoryIdsBean);
}
Can anyone please look into this, and help me out?
The problem here is, you are testing the controller by directly invoking the controller method
topicController.listCategoryTopics(categoryIdsBean).
This is not a good approach to test controller functionality. What you should do is use MicronautTest. MicronautTest will start an embedded server. Now you can use an HTTP client to hit the endpoint and retrieve the result.
Your code needs to be changed to something around the lines as below.
#MicronautTest
class HelloWorldTest {
#Inject
#Client("/")
RxHttpClient client;
#Test
public void should_list_category_topics() {
// Test Data
CategoryIdsRequestBean categoryIdsBean = new CategoryIdsRequestBean();
IdBean idBean = new IdBean();
idBean.setId(ID_1);
categoryIdsBean.setCategoryIds(Arrays.asList(idBean));
HttpRequest<String> request = HttpRequest.POST("/XXX", categoryIdsBean);
ResponseEntity<List<CategoryTopicBean>> retrieve = client.toBlocking().retrieve(request, ResponseEntity.class);
categoryIdsBean = new CategoryIdsRequestBean();
categoryIdsBean.setCategoryIds(Arrays.asList(idBean, idBean, idBean, idBean, idBean, idBean));
responseEntity = topicController.listCategoryTopics(categoryIdsBean);
}
}
For the exception case scenario, as the exception handler returns ResponseEntity<String>, you would need to make a minor change in the above code.
ResponseEntity<String> retrieve = client.toBlocking()
.retrieve(request, ResponseEntity.class);
If your controller calls any other service, do not forget to mock the behavior.
Can any one please help me to write a unit test case for this method returning RxJava Future object , I am able to write and mock for a method returning Single.
public Future<JsonObject> fetchVendorDetailsVendorIdAsFuture(String serviceURI, Map<String, String> headerMap) {
if(vbConnectorCircuitBreaker == null){
vbConnectorCircuitBreaker= CircuitBreakers.getVbConnectorCircuitBreaker();
}
return vbConnectorCircuitBreaker.execute(future -> {
// get ok http client
OkHttpClient client = okHTTPClientHelper.getOkHTTPClient();
if(client != null){
try{
MediaType mediaType = MediaType.parse("application/json");
Headers headers = Headers.of(headerMap);
Request request = new Request.Builder()
.url(serviceURI)
.get()
.headers(headers)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
public void onResponse(Call call, Response response)
throws IOException {
String jsonData = response.body().string();
JsonObject jsonObject = new JsonObject(jsonData);
future.complete(jsonObject);
}
public void onFailure(Call call, IOException e) {
future.complete(null);
}
});
} catch(Exception exception) {
future.complete(null);
}
} else {
future.complete(null);
}
});
}
You can try using okhttp's MockWebServer.
That way, your Call can emit a real http request and you will be able to handle the server's response.
You can create the mocked server's response yourself using mockWebServer.enqueue(new MockResponse() ... )
There are a lot of different ways to write tests for this kind of problem and here is my suggestion:
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
// other imports omitted
#ExtendWith(VertxExtension.class)
#Slf4j
public class VendorDetailsTest {
private VendorDetailsVerticle sut;
private MockWebServer mockWebServer;
#BeforeEach
public void setUp() {
sut = new VendorDetailsVerticle();
mockWebServer = new MockWebServer();
}
#Test
public void testExecuteService(final Vertx vertx, final VertxTestContext testContext)
throws InterruptedException {
// given -----
final JsonObject serverResponsePayload = new JsonObject().put("futureCompleted", true);
mockWebServer.enqueue(new MockResponse()
.setBody(serverResponsePayload.encode())
.setResponseCode(200)
.setHeader("content-type", "application/json"));
// when -----
final Future<JsonObject> jsonObjectFuture =
sut.fetchVendorDetailsVendorIdAsFuture(mockWebServer.url("/").toString(), new HashMap<>());
// then -----
final RecordedRequest recordedRequest = mockWebServer.takeRequest();
assertEquals("GET", recordedRequest.getMethod());
assertEquals(1, mockWebServer.getRequestCount());
testContext.assertComplete(jsonObjectFuture)
.map(val -> {
assertEquals("{'futureCompleted': true}", val.encode());
testContext.completeNow();
return val;
})
.onComplete(onComplete -> {
assertTrue(onComplete.succeeded());
log.info("done");
})
.onFailure(onError -> Assertions.fail());
}
}
This test will of course need a little bit of customization to run in your project, but I hope it will give a picture on how to approach testing RxJava's futures.
I am new to JUNIT and using RestTemplate to call my service, I'm getting 200 response for the same. But, I can't test the class using JUnit. Tried different approaches and getting 400 and 404. I want to post the request body (json) and test the status. Please let me know if there is any issue.
/**
* Rest client implementation
**/
public class CreateEmailDelegate implements CDM {
#Autowired
private RestTemplate restTemplate;
private String url = "http://example.com/communications/emails";
public ResponseEntity<CDResponse> createEmail(CDMEmailRequest cDRequest) throws UnavailableServiceException, InvalidInputException {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("SR_API_Key", SR_API_KEY);
httpHeaders.set("consumerIdentification", CONSUMER_IDENTIFICATION);
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity< CDMEmailRequest > cDRequestEntity = new HttpEntity<>( cDRequest, httpHeaders);
ResponseEntity< CDResponse > cDResponse = null;
try {
cDResponse = restTemplate.postForEntity(url, cDRequestEntity, CDResponse.class);
} catch (Exception e) {
LOGGER.error(e.getMessage());
throw e;
}
return cDResponse;
}
}
My Test class which return 404 status instead of 200
#RunWith(SpringJUnit4ClassRunner.class)
public class CreateEmailCommunicationDelegateTest {
#Before
public void setup() {
httpHeaders = new HttpHeaders();
httpHeaders.set("SR_API_Key", SR_API_KEY);
httpHeaders.set("consumerIdentification", CONSUMER_IDENTIFICATION);
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.wac);
this.mockMvc = builder.build();
}
public void testResponse() throws Exception, HttpClientErrorException, JsonProcessingException {
String url = "http://example.com/CommunicationDeliveryManagement-Service-1.0.0/communications/emails";
CDMEmailRequest anObject = new CDMEmailRequest();
ResultMatcher ok = MockMvcResultMatchers.status().isOk();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter();
String requestJson = ow.writeValueAsString(anObject);
System.out.println(requestJson);
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post(url).contentType(MediaType.APPLICATION_JSON_UTF8).content(requestJson);
this.mockMvc.perform(builder).andExpect(ok).andDo(MockMvcResultHandlers.print());
}
}
My Test class using TestRestTemplate instead MockMvc returns 400
#RunWith(SpringJUnit4ClassRunner.class)
public class CreateEmailCommunicationDelegateTest {
#Before
public void setup() {
httpHeaders = new HttpHeaders();
// rest headers as above
}
#Test
public void testResponse() throws Exception, HttpClientErrorException, JsonProcessingException {
String url = "http://example.com/CommunicationDeliveryManagement-Service-1.0.0/communications/emails";
String username = "";
String password = "";
HttpEntity<CDMEmailRequest>
cDEntity = new HttpEntity<>(httpHeaders);
restTemplate = new TestRestTemplate(username, password);
responseEntity =
restTemplate.exchange(url, HttpMethod.POST, cDEntity,
CDResponse.class);
assertNotNull(responseEntity);
assertEquals(HttpStatus.OK,
responseEntity.getStatusCode());
}
}
I think you're trying to implement an integration test instead of an unit test, there is quite difference. MockMvc should be used to implement unit tests and TestRestTemplate for integration tests. You can't neither use it for testing a Client implementation.
See Unit and Integration Tests in Spring Boot
If you are working with Spring Boot you could achieve your goal using another approach see this question Spring boot testing of a rest client using #RestClientTest.