Hot questions for Using Mockito in json

Question:

I want to write an unit test to test a JsonProcessingException. This exception can happen on the line: mapper.writeValueAsString().

public void myMethod(Args...) {
        try {
               ObjectMapper mapper = new ObjectMapper();
                mapper.configure(SerializationFeature.INDENT_OUTPUT, true);

                Document.parse(mapper.writeValueAsString(testObject)));
            }
        } catch (JsonProcessingException e) {
            log.error("Error parsing the object to json string. ", e);
        } 
}

and here is my unit test:

public class Test {

    @Mock
    private ObjectMapper              mapper;

    @InjectMocks
    ClassUnderTest            sut;

    @Test
    public void testJsonProcessingException() throws     JsonProcessingException {
        when(mapper.writeValueAsString(Mockito.any())).thenThrow(new     JsonProcessingException("Error parsing the object to json string. "){
            private static final long serialVersionUID = 1L;});
        sut.myMethod(args...);
    }

}

The problem is the mapper which i have mocked will not be used (i get unnecessary Mockito stubbings failure) as the mapper is again initialized inside the method. How can i unit test such a situation with mockito?


Answer:

mapper is not a field/dependency of the instance under test. It is a local variable created within the method :

 public void myMethod() {
        try {
            ObjectMapper mapper = new ObjectMapper();
         ...
}

So, @InjectMocks will be helpless.

I can propose you two alternatives :

1) You could pass mapper as an argument by changing the signature of the method :

 public void myMethod(ObjectMapper mapper) {
  ...
 }

In this way you could pass the mocked ObjectMapper as argument of the tested method.

If this method is often invoked, passing a boiler plate parameter may be annoying and makes the code less readable.

2) Another way is providing a way to set ObjectMapper via the constructor of the class under test.

public class ClassUnderTest{

   private ObjectMapper objectMapper;
   ...
   public ClassUnderTest(ObjectMapper objectMapper){
      this.objectMapper = objectMapper;
  }
  ...
}

Question:

Let's say I have a class called Api and it has a method:

public class Api{
    public HttpResponse<JsonNode> request() {
        try {
            return Unirest.get("http://localhost:8080").header("accept", "application/json").asJson();
        } catch (UnirestException e) {
            throw new RuntimeException(e);
        }
    }
}

And I have a class:

public class Dao(){

    private Api api;
    public Dao(Api api){
        this.api = api;
    }

    public Integer test(){
        Integer result = api.request().getInteger("result");
        return result + 100;
    }
}

In my test I want to test my business logic based on the response my API.request method returns.

Something like:

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.stub;
import org.json.JSONObject;
import com.mashape.unirest.http.HttpResponse;

public class ApiTest {
    private API api = mock(API.class);
    public void test() {
        HttpResponse<JsonNode> response = null;
        JSONObject result = new JSONObject();
        response.getBody().getObject();
        stub(api.request("")).toReturn(response);
        Dao dao = new Dao(api);
        assertTrue(dao.test() > 100);
    } 
}

How do I instantiate the HttpResponse with a Body "{ number: 10 }" to be able to return it with the mock?


Answer:

This is how I do it:

public class Dao() {

    private Api api;

    public Dao(Api api){
        this.api = api;
    }

    public Integer test(){
        // this is probably not good style
        //    should iterate through pulling out each piece 
        Integer result = api.request().getBody().getObject().getInteger("result");
        return result + 100;
    }
}

public class ApiTest {
    private API api = mock(API.class);
    public void test() {
        JsonNode json = new JsonNode("{\"result\":10}");

        HttpResponse<JsonNode> mockResponse = mock(HttpResponse.class);
        when(mockResponse.getCode()).thenReturn(200);
        when(mockResponse.getBody()).thenReturn(json);

        when(api.request(anyString())).thenReturn(mockResponse);

        Dao dao = new Dao(api);
        // this should be done more carefully as well, check code/body/etc..
        assertTrue(dao.test() > 100);
    }
}

Question:

After upgrading my rest service from Spring Boot 1.5.10 to 2.0.0 I encountered my tests failing which passed before.

Following Scenario:

import org.mockito.internal.matchers.Null;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

...

.andExpect(jsonPath("img").value(Null.NULL))

Fails now in Spring MVC 5 with following message:

java.lang.AssertionError: JSON path "img" Expected :isNull() Actual :null

What is the correct way in Spring MVC 5 to assert that the value of the jsonPath is null?


Answer:

Answering my own question as I found the solution by myself.

You have to use the correct Matcher, in my case org.hamcrest.core.IsNull

So I had to change to

import org.hamcrest.core.IsNull;
...
andExpect(jsonPath("img").value(IsNull.nullValue()))

Question:

I want to mock http POST with json data.

For GET method I succedded with the following code:

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

when(request.getMethod()).thenReturn("GET");
when(request.getPathInfo()).thenReturn("/getUserApps");
when(request.getParameter("userGAID")).thenReturn("test");
when(request.getHeader("userId")).thenReturn("xxx@aaa-app.com");

My problem is with http POST request body. I want it to contain application/json type content.

Something like this, but what should be the request params to answer json response?

HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);

when(request.getMethod()).thenReturn("POST");
when(request.getPathInfo()).thenReturn("/insertPaymentRequest");
when( ????  ).then( ???? maybe ?? // new Answer<Object>() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        new Gson().toJson("{id:213213213 , amount:222}", PaymentRequest.class);
        }
    });

Or maybe "public Object answer..." is not the correct method to use for Json return.

usersServlet.service(request, response);

Answer:

Post request body is accessed either via request.getInputStream() or via request.getReader() method. These are what you need to mock in order to provide your JSON content. Make sure to mock getContentType() as well.

String json = "{\"id\":213213213, \"amount\":222}";
when(request.getInputStream()).thenReturn(
    new DelegatingServletInputStream(
        new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8))));
when(request.getReader()).thenReturn(
    new BufferedReader(new StringReader(json)));
when(request.getContentType()).thenReturn("application/json");
when(request.getCharacterEncoding()).thenReturn("UTF-8");

You can use DelegatingServletInputStream class from Spring Framework or just copy its source code.

Question:

I am trying to mock a call to the getJSONFile() that returns someJson object from a json file.

public class NameClass{
    public String getValues() throws Exception 
            {
                HelpingClass e=new HelpingClass();
                String name=" ";
                String age=" "; 
                String gender=" ";
                JSONObject json= e.getJSONFile();
                json.getString("name");
                json.getString("age");
                json.getString("gender");
                System.out.println("Request received is "+json);
                ------ I have some other logic with JSON received and return string...

    }
}

This is my getJSONFile()

public class HelpingClass{
     public JSONObject getJSONFile() throws Exception
        {
            System.out.println("Getting JSON file");
            File f = new File("src/main/resources/input.json");
            JSONObject json ;
            InputStream is = new FileInputStream(f);
            String jsonTxt = IOUtils.toString(is);
            json=new JSONObject(jsonTxt);
            return json;
        }
}

This is my test case

@RunWith(MockitoJUnitRunner.class)
public class TestValues {

    @InjectMocks NameClass names;
    @Mock HelpingClass help;
    @Test
    public void testgetValues() throws Exception
    {
        //I am expecting this output
        JSONObject addValues=new JSONObject();
        addValues.put("name", "Rahul");
        addValues.put("age", "30");
        addValues.put("gender","male");
        //this is what i am returning when help.getJSONFile is called
        JSONObject json=new JSONObject();
        json.put("name", "Rahul");
        json.put("age", "30");
        json.put("gender", "male");
        //Mocking this call and returning my json
        Mockito.when(help.getJSONFile()).thenReturn(json);
        String s=names.getValues();
        Assert.assertEquals(addValues.toString(),s);
    }
}

Now instead of returning the created json it is returning me my JSON in the file like name is Rohit age is 28 and gender is male.

Why is this mocking not working? Technically it should call mocked method and return values in json object of JSONObject class i.e. Rahul, 30 years and male.

Where am I going wrong?


Answer:

Well @InjectMocks wont work if you are instantiating the HelperClass inside of the method.

You would have to make it an instance variable or use a getter which you would then mock:

public class NameClass{

  public String getValues() throws Exception 
  {
            HelpingClass e= getHelpingClass();
            ....
  }

  HelpingClass getHelpingClass(){
      return new HelpingClass();
  }
}

and in your test use @Spy like following:

public class TestValues {

    @InjectMocks 
    @Spy
    NameClass names;
    @Mock HelpingClass help;
    @Test
    public void testgetValues() throws Exception
    {
        doReturn(help).when(names).getHelpingClass();
        Mockito.when(help.getJSONFile()).thenReturn(json);
        ...
     }
   }

Question:

i have a custom JsonSerialzier to serialize dates in a special format:

public class CustomDateJsonSerializer extends JsonSerializer<Date> {

    @Override
    public void serialize(Date value, JsonGenerator gen, SerializerProvider arg2) throws IOException, JsonProcessingException {
        String outputDateValue;
        //... do something with the Date and write the result into outputDateValue
        gen.writeString(outputDateValue);
    }
}

It works fine but how can i test my code with JUnit and Mockito? Or rather how can i mock the JsonGenerator and access the result?

Thank you for your help.


Answer:

You can do something like this:

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.times;

@RunWith(MockitoJUnitRunner.class)
public class CustomDateJsonSerializerTest {

    @Mock
    private JsonGenerator gen;

    @Test
    public void testOutputDateValue() {
        CustomDateJsonSerializer serializer = new CustomDateJsonSerializer();
        serializer.serialize(new Date(), gen, null /*or whatever it needs to be*/);

        String expectedOutput = "whatever the correct output should be";
        verify(gen, times(1)).writeString(expectedOutput);
    }

}

Question:

In the spirit of learning Spring-boot with Spring Mock-Mvc and/or Mockito, I have build a small API with the potential of getting complicated later when I learn more stuff.

The Theme is "A Song of Ice and Fire" or "Game Of Thrones". So far I only have one package where you can add, delete and get different kingdoms when you send requests to "/Westeros". Each Kingdom only require a name property for now.

The Database I am using is Neo4J.

I uploaded the code to github, here is the link https://github.com/darwin757/IceAndFire

The Problem: The Porblem is in my KingdomControllerTest class in the methods addKingdomTest and updateKingdomTest

    @Test
public void addKingdomTest() throws Exception {

    mockMvc.perform(post("/Westeros").contentType(MediaType.APPLICATION_JSON_UTF8).content("{\"name\":\"Dorne\"}")
            .accept(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isCreated()).andReturn();

    //This part of the test is not working
    mockMvc.perform(get("/Westeros/Dorne").contentType(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isOk())
            .andExpect(jsonPath("$.name").value("Dorne"));

}

@Test
public void updateKingdomTest() throws Exception {

    mockMvc.perform(post("/Westeros").contentType(MediaType.APPLICATION_JSON_UTF8).content("{\"name\":\"Dorne\"}")
            .accept(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isCreated());

    mockMvc.perform(put("/Westeros/Dorne").contentType(MediaType.APPLICATION_JSON_UTF8)
            .content("{\"name\":\"theReach\"}").accept(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isOk());

    //This Part of the test is not working
    mockMvc.perform(get("/Westeros/Dorne").contentType(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isOk())
            .andExpect(jsonPath("$.name").value("the Reach"));

}

As you see, when I ask the API to create a new Kingdom, it returns a 201 isCreated or 200 isOK, but when I send a get request I get back a "No value at JSON path exception"

java.lang.AssertionError: No value at JSON path "$.name", exception: json can not be null or empty
at org.springframework.test.util.JsonPathExpectationsHelper.evaluateJsonPath(JsonPathExpectationsHelper.java:245)
at org.springframework.test.util.JsonPathExpectationsHelper.assertValue(JsonPathExpectationsHelper.java:99)
at org.springframework.test.web.servlet.result.JsonPathResultMatchers$2.match(JsonPathResultMatchers.java:100)
at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:171)
at com.example.Westeros.Kingdoms.KingdomControllerTest.addKingdomTest(KingdomControllerTest.java:95)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

I am Very new to Spring in general, and I did not find proper guides on spring Mock-Mvc or Mockito. I have no idea what is wrong, is it my syntax or my API? any help would be appreciated.

Here is the entire Class:

package com.example.Westeros.Kingdoms;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.stubbing.OngoingStubbing;
import org.springframework.beans.factory.annotation.Autowired;
import 
org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.mockito.Mockito.when;
import static org.junit.Assert.*;
import static org.mockito.Mockito.any;

import static 
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static 
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static 
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static 
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static 
org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static 
org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static 
org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static 
org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;

import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;

//TODO Major refactor required to clean up this class and consider the 
testing strategy 
@RunWith(SpringRunner.class)
@SpringBootTest
public class KingdomControllerTest {

@Autowired
private WebApplicationContext context;

private MockMvc mockMvc;

@MockBean
private KingdomService kingdomServiceMock;

@Before
public void setup() {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}

@Test
public void getAllKingdomsTest() throws Exception {

    List<Kingdom> kingdoms = setUpAListOfKingdoms();

    when(kingdomServiceMock.getAllKingdoms()).thenReturn(kingdoms);

    mockMvc.perform(get("/Westeros").accept(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
            .andExpect(jsonPath("$[0].name").value("TheNorth"))
            .andExpect(jsonPath("$[1].name").value("TheRiverlands"));
}


@Test
public void getKingdomTest() throws Exception {

    Kingdom theNorth = setUpAKingdom("TheNorth");
    kingdomServiceMock.addKingdom(theNorth);

    when(kingdomServiceMock.getKingdom("TheNorth")).thenReturn(theNorth);

    mockMvc.perform(get("/Westeros/TheNorth")).andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
            .andExpect(jsonPath("$.name").value("TheNorth"));
}

// FIXME This test is returning 201 isCreated,
// but if I perform a get after I get an assertion exception that the variable
// name is empty.
@Test
public void addKingdomTest() throws Exception {

    mockMvc.perform(post("/Westeros").contentType(MediaType.APPLICATION_JSON_UTF8).content("{\"name\":\"Dorne\"}")
            .accept(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isCreated()).andReturn();

    //This part of the test is not working
    mockMvc.perform(get("/Westeros/Dorne").contentType(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isOk())
            .andExpect(jsonPath("$.name").value("Dorne"));

}

@Test
public void updateKingdomTest() throws Exception {

    mockMvc.perform(post("/Westeros").contentType(MediaType.APPLICATION_JSON_UTF8).content("{\"name\":\"Dorne\"}")
            .accept(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isCreated());

    mockMvc.perform(put("/Westeros/Dorne").contentType(MediaType.APPLICATION_JSON_UTF8)
            .content("{\"name\":\"theReach\"}").accept(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isOk());

    //This Part of the test is not working
    mockMvc.perform(get("/Westeros/Dorne").contentType(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isOk())
            .andExpect(jsonPath("$.name").value("the Reach"));

}

@Test
public void deleteKingdomTest() throws Exception {

    mockMvc.perform(post("/Westeros").contentType(MediaType.APPLICATION_JSON_UTF8).content("{\"name\":\"theVale\"}")
            .accept(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isCreated());

    mockMvc.perform(delete("Westeros/theVale").contentType(MediaType.APPLICATION_JSON_UTF8))
            .andExpect(status().isNotFound());

}

// FIXME refer to the KingdomController class, the method should be moved to
// another class more suited to it's purpose
@Test
public void deleteAlltest() {
}

private List<Kingdom> setUpAListOfKingdoms() {

    Kingdom theNorth = setUpAKingdom("TheNorth");
    Kingdom theRiverlands = setUpAKingdom("TheRiverlands");

    List<Kingdom> kingdoms = new ArrayList<Kingdom>();

    kingdoms.add(theNorth);
    kingdoms.add(theRiverlands);

    // FIXME wrong place for this code but I can't find another
    kingdomServiceMock.addKingdom(theNorth);
    kingdomServiceMock.addKingdom(theRiverlands);

    return kingdoms;

}

private Kingdom setUpAKingdom(String name) {

    Kingdom kingdom = new Kingdom(name);
    return kingdom;
}

}

Thank you in advance.


Answer:

Ok I checked your github and the problem is that you are mocking your KingdomService:

@MockBean
private KingdomService kingdomServiceMock;

But in these failing tests you are not asserting any behaviour for the mocked methods that are called. The default response for a mocked class is to return null hence the kingdomService.getKingdom(name) method here returns null always:

@RequestMapping("/Westeros/{name}")
    public Kingdom getKingdom(@PathVariable String name) {
        return kingdomService.getKingdom(name);
}

You probably want to do more of an integration test in which case I would say you don't want to mock the service.

Therefore in your get tests that are currently working instead of setting up a mock behavior you should autowire the repository and actually add the Kingdoms you want to test for, e.g.:

@Autowired
KingdomRepository kingdomRepository;

@Test
@Transactional
    public void getAllKingdomsTest() throws Exception {

        List<Kingdom> kingdoms = setUpAListOfKingdoms();

        kingdomRepository.saveAll(kingdoms);
        kingdomRepository.flush();

        mockMvc.perform(get("/Westeros").accept(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(jsonPath("$[0].name").value("TheNorth"))
                .andExpect(jsonPath("$[1].name").value("TheRiverlands"));
    }

The @Transactional annotation ensures that the interactions you have with the database are rolled back at the end of each test.

EDIT: You should also make sure your repositories are implementing JpaRepository not PagingAndSortingRepository. This way you can call the .flush() method on your repo to ensure all pending changes to the DB are instantly flushed to it.

Question:

I am trying to test following code with JMockito.

Code

@RunWith(MockitoJUnitRunner.class)
public class StepFunctionClientTest {

    private static final String TEST_STATE_MACHINE_ARN = "testStateMachineArn";
    private static final String TEST_EXECUTION_ARN = "testExecutionArn";
    private ObjectNode additionalData;
    private ObjectMapper objectMapper;

    @Mock AWSStepFunctions mockAWSStepFunctions;
    private StepFunctionClient stepFunctionClient;

    @Before
    public void setUp() throws Exception {
        stepFunctionClient = new StepFunctionClient(mockAWSStepFunctions, TEST_STATE_MACHINE_ARN);
        additionalData = TestData.getObjectNode(TestData.ADDITIONAL_DATA);
        objectMapper = new ObjectMapper();
    }

    @Test(expected=InternalServiceException.class)
    public void test() throws Exception {
        StartExecutionResult result = new StartExecutionResult();
        result.setExecutionArn(TEST_EXECUTION_ARN);

        when(mockAWSStepFunctions.startExecution(any())).thenThrow(new JsonProcessingException("Error"){});

        stepFunctionClient.startExecution(
                TEST_ORDER_ID,
                TEST_PAYMENT_STATUS,
                null,
                false);
    }
}

Error

[junit] Testcase: test(com.project.orderservice.client.StepFunctionClientTest):       Caused an ERROR
[junit] Unexpected exception, expected<com.project.commons.exceptions.InternalServiceException> but was<org.mockito.exceptions.base.MockitoException>
[junit] java.lang.Exception: Unexpected exception, expected<com.project.commons.exceptions.InternalServiceException> but was<org.mockito.exceptions.base.MockitoException>
[junit]     at org.mockito.internal.runners.DefaultInternalRunner$1.run(Unknown Source)
[junit]     at org.mockito.internal.runners.DefaultInternalRunner.run(Unknown Source)
[junit]     at org.mockito.internal.runners.StrictRunner.run(Unknown Source)
[junit]     at org.mockito.junit.MockitoJUnitRunner.run(Unknown Source)
[junit] Caused by: org.mockito.exceptions.base.MockitoException: 
[junit] Checked exception is invalid for this method!
[junit] Invalid: com.project.orderservice.client.StepFunctionClientTest$1: Error
[junit]     at com.project.orderservice.client.StepFunctionClientTest.test(StepFunctionClientTest.java:157)

What i am missing here?


Answer:

JsonProcessingException is a checked exception, meaning that if you want to throw it, the method has to have it in its signature. Otherwise you get the error above:

Checked exception is invalid for this method!

You can throw only the checked exception listed here -> StartExecutionRequest.

Or a RuntimeException.

Question:

I have a piece of code that I want to test which takes a JSON string and uses the object from the JSON string to call a method

@RequestMapping(value = "/cancel", method = RequestMethod.POST, produces = "application/json")
    public ReservationCancelResponseType cancel(@RequestBody CancelReservationRequest request) {
        ReservationCancelResponseType result = null;

        for(BrandEnum brand : request.getBrands()) {
            switch(brand) {
            case BRAND_NAME:
                result = service.cancel(request);
                break;
            }
        }

        return result;
    }

I am trying to call this using the following code

@Test
public void testCancel() throws Exception {
    ReservationCancelResponseType responseType = new ReservationCancelResponseType();

    CancelReservationRequest request = new CancelReservationRequest();
    List<BrandEnum> brands = new ArrayList<>();
    brands.add(BrandEnum.BRAND_NAME);
    request.setBrands(brands);

    String requestString = objectMapper.writeValueAsString(request);

    when(service.cancel(request)).thenReturn(responseType);

    this.mockMvc.perform(post("/cancel")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestString)
            ).andExpect(status().isOk());
}

I think the reason this is not working is because in the when().thenReturn() call, I am passing in an object but in the rest call I am passing in a String version of this object created by the objectMapper so these are different so I am getting null for the when().thenReturn() call

Is this correct and if so how would you suggest i resolve this?


Answer:

Assuming that the instance of service which is used by your controller in the test flow definitely the same instance as you are mocking in your test then the likeliest cause of the issue is CancelReservationRequest's implementation of equals(). Either it has no equals() or its equals() implementation is returning false when Mockito attempts to compare the instance expected by your when/then call with the instance used inside your controller method.

You could verify this by changing ...

when(service.cancel(request)).thenReturn(responseType)

... to :

when(service.cancel(Mockito.any(CancelReservationRequest.cla‌ss))).thenReturn(res‌​ponseType)

If the service.cancel() method returns your response type then you'll know the issue is with CancelReservationRequest's equality check. The fix for this is to implement an equals() method which allows Mockito to correctly compare the instance expected by your when/then call with the instance used inside your controller method. You could even use Mockito.refEq() if creating a custom equals() method is not a runner.

Question:

I would like to pass an input file to MockMVC perform statement. Please find the code snippet below:

@Test
public void test() throws Exception {

   this.mockMvc.perform(post("/tax_rates/v1/quotations")
           .contentType(MediaType.APPLICATION_JSON_UTF8).pathInfo("/src/main/resources/input.json"))
           .andExpect((ResultMatcher) status().is2xxSuccessful());

}

When I tried using pathInfo variable I get the error as below:

HttpMessageNotReadableException: Required request body is missing:

which i guess it means payload is not getting passed?

Any suggestions would help me.

Regards, Sunil


Answer:

we can pass json input as content :

ObjectMapper mapper=new ObjectMapper();
String jsonString=mapperwriteValueAsString(mapper.readValue(new File("path/to/file",Object.class));
 this.mockMvc.perform(post("/tax_rates/v1/quotations")
           .contentType(MediaType.APPLICATION_JSON_UTF8).content(jsonString))
           .andExpect(status().is2xxSuccessful());

If you want to pass MultipartFile as input. Here is the link:

Using Spring MVC Test to unit test multipart POST request

Question:

I am writing some tests for spring mvc application.

I got the response as follows.

-> curl -X POST -H"Content-type:application/json; charset=utf-8" localhost:8080/mvc/addBlacklist.do -d '{"id": "1", "imsi": "test"}'

{"id":0,"imsi":"18192729090","name":"xiaoshao","monitors":null}

But when my test like this one.

Blacklist blackList = new Blacklist();
    blackList.setId(0);
    blackList.setImsi("18192729090");
    blackList.setName("xiaoshao");

    when(blacklistService.add(any())).thenReturn(blackList);

    mockMvc.perform(post("/addBlacklist")
            .content(TestUtil.convertObjectToJsonBytes(blackList))
            .contentType(TestUtil.APPLICATION_JSON_UTF8))
            .andExpect(status().is(200))
            .andExpect(jsonPath("$.imsi", is("xiaoshao")));

It emit an exception like this.

java.lang.NoClassDefFoundError: com/jayway/jsonpath/InvalidPathException
at org.springframework.test.web.servlet.result.JsonPathResultMatchers.<init>(JsonPathResultMatchers.java:53)

Answer:

You are missing a dependency for the test. Add this to your maven pom.

<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.2.0</version>
   <scope>test</scope>
</dependency>