Testing Unirest with PowerMockito - java

I want to test a method that sends a get request to a server using Unirest
I have used PowerMockito and Mockito to test some methods, but when I try to do it on the method below, I get null pointer exception.
This is the method I want to test:
public HttpResponse<JsonNode> jsonRequest(String path) {
HttpResponse<JsonNode> jsonResponse = null;
try {
jsonResponse = Unirest
.get(path)
.header("accept", "application/json")
.asJson();
} catch (UnirestException e) {
System.out.println("Server is unreachable at the moment. Please try again later");
}
return jsonResponse;
}
And this is the test case:
#RunWith(PowerMockRunner.class)
#PrepareForTest(Unirest.class)
public class TestModel {
#Mock
private GetRequest getRequest;
#Mock
private HttpResponse<JsonNode> httpResponse;
#InjectMocks
DatabaseModel databaseModel;
#Test
public void testController() {
PowerMockito.mockStatic(Unirest.class);
JsonNode jsonNode = mock(JsonNode.class);
when(Unirest.get("12345")).thenReturn(getRequest);
when(getRequest.asJson()).thenReturn(httpResponse);
when(httpResponse.getStatus()).thenReturn(200);
assertEquals(200, databaseModel.jsonRequest("12345").getStatus());
}
So I expect to get 200 (Or any status\body I will decide on)
But for some reason I get java.lang.NullPointerException
which directs me to this line:
.asJson();
in the getRequest.
Thanks in advance

You were almost there, you forgot to set-up the header:
when(Unirest.get("12345")).thenReturn(getRequest);
when(getRequest.header("accept", "application/json").thenReturn(getRequest)
when(getRequest.asJson()).thenReturn(httpResponse);
when(httpResponse.getStatus()).thenReturn(200);

Related

Assertion error expected 500 but got 400 Junit testing

Controller:
#ApiOperation(value = " update record", response = APIResponse.class)
#ApiResponses(value = {#ApiResponse(code =200, message = "OK"),
#ApiResponses(value = {#ApiResponse(code =500, message = "Internal server error"),
#ApiResponses(value = {#ApiResponse(code =404, message = "NO_RECORD")})
#PutMapping(value = "/update/{id}")
#ResponseBody
public ResponseEntity<APIResponse> updateRecord(HttpServletRequest request, #RequestBody RecordDTO input, #PathVariable(value="id") int code){
APIResponse response = null;
try{
response = service.updateRecord(code, input);
}
catch(JSONException e){
log.error("Error Parsing JSON");
response = new APIResponse(HttpStatus.INTERNAL_SERVER_ERROR, ERROR_JSON_PARSING, ERROR);
}
return new ResponseEntity<>(response, HttpStatus.OK);
}
my test case foor controller:
#Test
public void update() throws Exception{
RecordDTO recordDto = new RecordDTO();
Object mapper = new ObjectMapper();
String value = mapper.writeValueAsString(StationDTO);
given(service.updateRecord(anyInt(), any(RecordDTO.class))).willThrow(JSONException.class);
mockMvc.perform(put(baseUrl + "/update/12")
.contentType(MediaType.APPLICATION_JSON).content(value))
.andExpect(status().isInternalservererror())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.status",Matchers.is("INTERNAL_SERVER_ERROR")))
.andExpect(jsonPath("$.message",Matchers.is("ERROR_JSON_PARSING")))
.andExpect(jsonPath("$.resposeStatus",Matchers.is("ERROR")));
APIResponse response = new APIResponse(HttpStatus.OK, SUCCESS, SUCCESS, null);
given(service.updateRecord(anyInt(), any(RecordDTO.class))).willReturn(response);
mockMvc.perform(put(baseUrl + "/update/12")
.contentType(MediaType.APPLICATION_JSON).content(value))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.status",Matchers.is("OK")))
.andExpect(jsonPath("$.message",Matchers.is("SUCCESS")))
.andExpect(jsonPath("$.resposeStatus",Matchers.is("SUCCESS")));
}
DTO:
public class RecordDTO{
private String id;
private String name;
private String number;
}
I am getting java.lang assertion error expected 500 but was 200. I don't what is wrong with the test case.. Is there any other way to write the test case? Also can you recommend any platform from where i can gain knowledge of how to write test cases then do comment down. Thanks for the help!
Seems like your mocked service is not injecting into your controller.
Alternative solution (I assume you use Spring-Boot):
DisableAutowireRequireInitializer. This will prevent to load all dependencies inside your Controller.
Create inside your ControllerTest inner class: private static ServiceImplMock entends ServiceImpl
Now, override updateRecord method inside ServiceMock to do your testing cases
#Override
public APIResponse updateRecord(int code, RecordDTO input) throws JSONException {
if(code == 12) throw new JSONException(...)
else your_business_logic
}
Now, add this ServiceImplMock into your #SpringBootTest
#SpringBootTest(classes = {
Controller.class,
ControllerTest.ServiceImplMock.class,
...
})
#AutoConfigureMockMvc
#ContextConfiguration( initializers = {DisableAutowireRequireInitializer.class })
class ControllerTest {
Now, your test cases should work (Remove given(...).willThrow(...); since we don't need it anymore)
Also can you recommend any platform from where i can gain knowledge of how to write test cases then do comment down
https://www.baeldung.com/junit
https://www.baeldung.com/spring-boot-testing
https://mkyong.com/spring-boot/spring-boot-junit-5-mockito/

How to get body as String from ServerResponse for test?

Say we have an instance of o.s.w.reactive.function.server.ServerResponse.
What is the proper way to fetch the contents of its body, in other words how to implement fetchBodyAsString function?
test(){
ServerResponse response = getResponseFromService("mock data");
String body = fetchBodyAsString(response);
assertEquals("hello", body);
}
Could you also elaborate a bit on why does ServerResponse have methods for everything (cookies(), headers(), statusCode()), but the response body? I guess there should be a way to get the body with writeTo() method, although it is absolutely vague how to use it.
I was digging around for something similar for unit testing purposes, and stitched together the below code. It's in Kotlin, but should be relatively easy to translate to Java and solve your problem (though it definitely does seem a bit hacky).
fun fetchBodyAsString(serverResponse: ServerResponse): String {
val DEFAULT_CONTEXT: ServerResponse.Context = object : ServerResponse.Context {
override fun messageWriters(): List<HttpMessageWriter<*>> {
return HandlerStrategies.withDefaults().messageWriters()
}
override fun viewResolvers(): List<ViewResolver> {
return Collections.emptyList()
}
}
// Only way I could figure out how to get the ServerResponse body was to have it write to an exchange
val request = MockServerHttpRequest.get("http://thisdoenstmatter.com").build()
val exchange = MockServerWebExchange.from(request)
serverResponse.writeTo(exchange, DEFAULT_CONTEXT).block()
val response = exchange.response
return response.bodyAsString.block()!!
}
Basically needed to create a fake MockServerWebExchange and have the ServerResponse write to it to translate it into a MockServerHttpResponse of which you can pull the response body out of fairly painlessly. This is definitely not elegant, but it works.
Also note, I didn't test the above function itself, just that it compiles. It should work though as the function's inner code is exactly what we're using.
As for your other questions about ServerResponse, I don't know the answers, but am curious about that as well!
As far as i know ServerResponse is used at the controller or router function.
For testing you can use WebTestClient
#Autowired
WebTestClient webTestClient;
#Test
void test() {
webTestClient.get()
.exchange()
.expectStatus()
.isOk()
.expectHeader()
.contentType(MediaType.APPLICATION_JSON)
.expectBody()
.jsonPath("data.name").isEqualTo("name");
}
or
#Autowired
WebTestClient webTestClient;
#Test
void test() {
FluxExchangeResult<String> result = webTestClient.get()
.exchange()
.returnResult(String.class);
int rawStatusCode = result.getRawStatusCode();
HttpStatus status = result.getStatus();
HttpHeaders responseHeaders = result.getResponseHeaders();
String stringResponseBody = result.getResponseBody().blockFirst();
}
This is based on Alan Yeung solution above, except in Java. There has to be a better 'native' way to do this without loading application context.
public class ServerResponseExtractor {
public static <T> T serverResponseAsObject(ServerResponse serverResponse,
ObjectMapper mapper, Class<T> type) {
String response = serverResponseAsString(serverResponse);
try {
return mapper.readValue(response, type);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public static String serverResponseAsString(ServerResponse serverResponse) {
MockServerWebExchange exchange = MockServerWebExchange.from(
MockServerHttpRequest.get("/foo/foo"));
DebugServerContext debugServerContext = new DebugServerContext();
serverResponse.writeTo(exchange, debugServerContext).block();
MockServerHttpResponse response = exchange.getResponse();
return response.getBodyAsString().block();
}
private static class DebugServerContext implements ServerResponse.Context {
#Override
public List<HttpMessageWriter<?>> messageWriters() {
return HandlerStrategies.withDefaults().messageWriters();
}
#Override
public List<ViewResolver> viewResolvers() {
return Collections.emptyList();
}
}
}
Another way to test the body inside a unit test is to cast the ServerResponse to an EntityResponse. This does show a warning for an unchecked cast but inside a controlled unit test I wasn't too worried about it. This just exposes the object that was set using bodyValue() before it is serialized. If you are trying to test the serialization of said body this might not work for your needs.
val entityResponse = serverResponse as EntityResponse<{ Insert Class of Body }>
val bodyObject = entityResponse.entity()

Testing Okhttp3 response body - java.lang.IllegalStateException: request == null

I have a method in class which does a HTTP GET call to get the response object and utilize this object further. The pesudo code is below:
public class ABC{
public method abc1(){
HttpUrl url = HttpUrl.parse("url").newBuilder()
.addPathSegment("path1")
.build();
Request request = new Request.Builder().url(url).build();
try (Response response = client.newCall(request).execute()) {
ResponseBody responseBody = response.body();
String body = responseBody.string();
//other logic
}catch (IOException e) {}
}
}
Now I am writing a unit test to test with different values in the response object (json object). This is as below:
public class ABCTest{
#Mock
private OkHttpClient mockHttpClient;
#Mock
private Call mockCall;
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
#Test
public void abc1Test(){
ResponseObjectInJson responseObjectInJson = new ResponseObjectInJson(); //this is a object from my POJO class that i create in order to be received as a response
JSONObject jsonObject = new
JSONObject(responseObjectInJson);
ResponseBody body =
ResponseBody.create(MediaType.parse("application/json"),new
Gson().toJson(jsonObject));
Response.Builder builder = new Response.Builder();
builder.code(200);
Response response = builder.body(body).build();
when(mockCall.execute()).thenReturn(response);
when(mockHttpClient.newCall(any(Request.class))).thenReturn(mockCall);
//call the abc1() method here to see the response and behaviour
}
}
The problem is, when i debug, it throws InvocationTargetException when building the response builder.body(body).build();
And shows java.lang.IllegalStateException: request == null. I understand that i need to set request in the Response.Builder because when i evaluate the expression builder.body(body) in debugger, in the result it shows headers and body, but request is null.
i.e., builder.request(//a request here)
My question is:
1. In response why the request is needed?
2. How to set this? because i am unable to mock since its final.
Thanks in advance

Mockito "thenThrow" doesn't throw the exception when expected

I have an issue when trying to test a class that represents a Rest Client. I'm using RestTemplate in Spring Boot.
This is the abstract RestClient class:
public abstract class RestClient {
...
public RestResponse sendPostRequest(URI baseUri, String resource, IRestRequest restRequest, ClassresponseClass)
throws ServerException, ClientException {
...
try {
RestTemplate restTemplate = new RestTemplate();
response = restTemplate.exchange(baseUri, HttpMethod.POST, getEntity(restRequest), responseClass);
result = response.getBody();
getLogger().debug("[{}] received", result);
return result;
} catch (HttpClientErrorException e) {
throw new ClientException(e.getCause());
} catch (HttpServerErrorException e) {
throw new ServerException(e.getCause());
} catch (Exception e) {
getLogger().error("Error with cause: {}.", e.getMessage());
}
...
}
}
This is the actual implementation:
public class ActualRestClient extends RestClient {
public RestResponse sendFetchFileRequest(URI baseUri, FetchFileRequest request) throws ServerException, ClientException {
return sendPostRequest(baseUri, "FETCH_FILE", request, RestResponse.class);
}
}
An this is the test:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ActualRestClient.class, RestClient.class})
public class ActualResRestClientTest {
private static final String REQUEST_URI = "something";
#InjectMocks
public ActualRestClient testee;
#Mock
private RestTemplate restTemplate;
#Test(expected = ServerException.class)
public void sendPostRequestWithResponseBody_throwsServerException() throws Exception {
HttpServerErrorException httpServerErrorException = new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR);
when(restTemplate.exchange(Mockito.any(URI.class), eq(HttpMethod.POST), Mockito.any(), eq(FetchFileRequest.class))).thenThrow(httpServerErrorException);
testee.sendFetchFileRequest(new URI(REQUEST_URI), new FetchFileRequest());
}
}
ClientException and ServerException are exceptions created by me by extending Exception class.
My problem is that in the RestClient class another Exception is catched(message:"URI is not absolute") instead of HttpServerErrorException and I can't understand why. Thank you!
As the commenter already expressed: doing new URI("something") already throws at you. But even when you pass a "valid" URI, your code will not work, as there is a misconception on your end. You see:
RestTemplate restTemplate = new RestTemplate();
response = restTemplate.exchange(baseUri, HttpMethod.POST, getEntity(restRequest), responseClass);
That code lives within a method of your class under test. But #InjectMocks works only for fields of classes.
In other words: when your production code gets executed, a new (completely different** ResponseTemplate instance is created. And therefore your mocking spec is irrelevant, because the method isn't invoked on your mock in the first place.
Two choices:
turn that local variable into a field of your class under test (then injecting should work)
or, as you are already using PowerMock(ito), you could use that mocking framework to intercept that call to new().
I suggest you rather use option one, and avoid to use the PowerMock(ito) extension altogether!

How to check String in response body with mockMvc

I have simple integration test
#Test
public void shouldReturnErrorMessageToAdminWhenCreatingUserWithUsedUserName() throws Exception {
mockMvc.perform(post("/api/users").header("Authorization", base64ForTestUser).contentType(MediaType.APPLICATION_JSON)
.content("{\"userName\":\"testUserDetails\",\"firstName\":\"xxx\",\"lastName\":\"xxx\",\"password\":\"xxx\"}"))
.andDo(print())
.andExpect(status().isBadRequest())
.andExpect(?);
}
In last line I want to compare string received in response body to expected string
And in response I get:
MockHttpServletResponse:
Status = 400
Error message = null
Headers = {Content-Type=[application/json]}
Content type = application/json
Body = "Username already taken"
Forwarded URL = null
Redirected URL = null
Tried some tricks with content(), body() but nothing worked.
You can call andReturn() and use the returned MvcResult object to get the content as a String.
See below:
MvcResult result = mockMvc.perform(post("/api/users").header("Authorization", base64ForTestUser).contentType(MediaType.APPLICATION_JSON)
.content("{\"userName\":\"testUserDetails\",\"firstName\":\"xxx\",\"lastName\":\"xxx\",\"password\":\"xxx\"}"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isBadRequest())
.andReturn();
String content = result.getResponse().getContentAsString();
// do what you will
#Sotirios Delimanolis answer do the job however I was looking for comparing strings within this mockMvc assertion
So here it is
.andExpect(content().string("\"Username already taken - please try with different username\""));
Of course my assertion fail:
java.lang.AssertionError: Response content expected:
<"Username already taken - please try with different username"> but was:<"Something gone wrong">
because:
MockHttpServletResponse:
Body = "Something gone wrong"
So this is proof that it works!
Spring MockMvc now has direct support for JSON. So you just say:
.andExpect(content().json("{'message':'ok'}"));
and unlike string comparison, it will say something like "missing field xyz" or "message Expected 'ok' got 'nok'.
This method was introduced in Spring 4.1.
Reading these answers, I can see a lot relating to Spring version 4.x, I am using version 3.2.0 for various reasons. So things like json support straight from the content() is not possible.
I found that using MockMvcResultMatchers.jsonPath is really easy and works a treat. Here is an example testing a post method.
The bonus with this solution is that you're still matching on attributes, not relying on full json string comparisons.
(Using org.springframework.test.web.servlet.result.MockMvcResultMatchers)
String expectedData = "some value";
mockMvc.perform(post("/endPoint")
.contentType(MediaType.APPLICATION_JSON)
.content(mockRequestBodyAsString.getBytes()))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.data").value(expectedData));
The request body was just a json string, which you can easily load from a real json mock data file if you wanted, but I didnt include that here as it would have deviated from the question.
The actual json returned would have looked like this:
{
"data":"some value"
}
Taken from spring's tutorial
mockMvc.perform(get("/" + userName + "/bookmarks/"
+ this.bookmarkList.get(0).getId()))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.id", is(this.bookmarkList.get(0).getId().intValue())))
.andExpect(jsonPath("$.uri", is("http://bookmark.com/1/" + userName)))
.andExpect(jsonPath("$.description", is("A description")));
is is available from import static org.hamcrest.Matchers.*;
jsonPath is available from import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
and jsonPath reference can be found here
Spring security's #WithMockUser and hamcrest's containsString matcher makes for a simple and elegant solution:
#Test
#WithMockUser(roles = "USER")
public void loginWithRoleUserThenExpectUserSpecificContent() throws Exception {
mockMvc.perform(get("/index"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("This content is only shown to users.")));
}
More examples on github
here a more elegant way
mockMvc.perform(post("/retrieve?page=1&countReg=999999")
.header("Authorization", "Bearer " + validToken))
.andExpect(status().isOk())
.andExpect(content().string(containsString("regCount")));
One possible approach is to simply include gson dependency:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
and parse the value to make your verifications:
#RunWith(SpringRunner.class)
#WebMvcTest(HelloController.class)
public class HelloControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private HelloService helloService;
#Before
public void before() {
Mockito.when(helloService.message()).thenReturn("hello world!");
}
#Test
public void testMessage() throws Exception {
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE))
.andReturn();
String responseBody = mvcResult.getResponse().getContentAsString();
ResponseDto responseDto
= new Gson().fromJson(responseBody, ResponseDto.class);
Assertions.assertThat(responseDto.message).isEqualTo("hello world!");
}
}
Here is an example how to parse JSON response and even how to send a request with a bean in JSON form:
#Autowired
protected MockMvc mvc;
private static final ObjectMapper MAPPER = new ObjectMapper()
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.registerModule(new JavaTimeModule());
public static String requestBody(Object request) {
try {
return MAPPER.writeValueAsString(request);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseResponse(MvcResult result, Class<T> responseClass) {
try {
String contentAsString = result.getResponse().getContentAsString();
return MAPPER.readValue(contentAsString, responseClass);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Test
public void testUpdate() {
Book book = new Book();
book.setTitle("1984");
book.setAuthor("Orwell");
MvcResult requestResult = mvc.perform(post("http://example.com/book/")
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody(book)))
.andExpect(status().isOk())
.andReturn();
UpdateBookResponse updateBookResponse = parseResponse(requestResult, UpdateBookResponse.class);
assertEquals("1984", updateBookResponse.getTitle());
assertEquals("Orwell", updateBookResponse.getAuthor());
}
As you can see here the Book is a request DTO and the UpdateBookResponse is a response object parsed from JSON. You may want to change the Jackson's ObjectMapper configuration.
Another option is:
when:
def response = mockMvc.perform(
get('/path/to/api')
.header("Content-Type", "application/json"))
then:
response.andExpect(status().isOk())
response.andReturn().getResponse().getContentAsString() == "what you expect"
You can use getContentAsString method to get the response data as string.
String payload = "....";
String apiToTest = "....";
MvcResult mvcResult = mockMvc.
perform(post(apiToTest).
content(payload).
contentType(MediaType.APPLICATION_JSON)).
andReturn();
String responseData = mvcResult.getResponse().getContentAsString();
You can refer this link for test application.
String body = mockMvc.perform(bla... bla).andReturn().getResolvedException().getMessage()
This should give you the body of the response. "Username already taken" in your case.
Another example is:
.andExpect(jsonPath("$").value(containsString("You have successfully deleted")));
The body response:
Body = You have successfully deleted a [Object] with ID: 1
Here is a more production ready way of doing it where you if you may have big json responses then you do not have to clutter your test files with json strings, just load them from static Resources folder and assert them directly.
#Test
#DisplayName("invalid fields")
void invalidfields() throws Exception {
String request = getResourceFileAsString("test-data/http-request/invalid-fields.json");
String response_file_path = "test-data/http-response/error-messages/invalid-fields.json";
String expected_response = getResourceFileAsString(response_file_path);
mockMvc.perform(evaluateRulesOnData(TRACKING_ID.toString(), request))
.andExpect(status().isBadRequest())
.andExpect(content().json(expected_response));
}
helper function to load test files from classpath
public static String getResourceFileAsString(String fileName) throws IOException {
Resource resource = new ClassPathResource(fileName);
File file = resource.getFile();
return new String(Files.readAllBytes(file.toPath()));
}
The expected response has an array with many elements in the list which are matched despite being in random order during each test run.

Categories

Resources