Question

I'm currently trying to create tests for our Servlets. I've mocked HttpServletRequest, HttpServletResponse and an object serving as a database handler. In the test for the doPost method, my wish is to compare the values from the Json object and the ArrayList sent to the database object.

The servlet:

public class WidgetStatusServlet extends HttpServlet {

private DBController db = new DBController();

    @Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    response.setContentType("application/json");
    JsonParser parser = new JsonParser();
    JsonElement tradeElement = parser.parse(request.getParameter("widgetdata"));
    JsonArray json = tradeElement.getAsJsonArray();
    Gson gson = new Gson();
    Type listType = new TypeToken<List<WidgetStatus>>() {
    }.getType();
    ArrayList<WidgetStatus> widgets = gson.fromJson(json, listType);

    Iterator<WidgetStatus> iterator = widgets.iterator();
    System.out.println("Widgets");
    while (iterator.hasNext()) {
        WidgetStatus node = (WidgetStatus) iterator.next();
        System.out.println(node);
    }

    db.addWidgetStatus(widgets);

}

The test:

public class WidgetStatusServletTest {

@Captor
private ArgumentCaptor<ArrayList<WidgetStatus>> captor;

private DBController dbMock = mock(DBController.class);
private HttpServletRequest request = mock(HttpServletRequest.class);
private HttpServletResponse response = mock(HttpServletResponse.class);
Repository repository = mock(Repository.class);`


    @Test
public void doPost_ShouldVerifyWidgetsIsProvided() throws Exception {
    final WidgetStatus widget1 = new WidgetStatus("id1", "div_id1", "5", "5", "1", "2", false);
    final WidgetStatus widget2 = new WidgetStatus("id2", "div_id2", "2", "1", "3", "1", true);

    when(request.getParameter("widgetdata")).thenAnswer(new Answer<String>() {
        @Override
        public String answer(InvocationOnMock invocation) throws Throwable {
            ArrayList<WidgetStatus> array = new ArrayList<WidgetStatus>();
            array.add(widget1);
            array.add(widget2);
            String json = new Gson().toJson(array);
            return json;
        }
    });
    new WidgetStatusServlet().doPost(request, response);
    verify(dbMock).addWidgetStatus(captor.capture());
    assertNotNull(captor.getAllValues());


}

When I'm running the test it gives me NullpointerException at the line

        verify(dbMock).addWidgetStatus(captor.capture());

What am I doing wrong? I've looked at several examples with the same usage of the ArgumentCaptor. Perhaps this is just a minor error?

Was it helpful?

Solution

If you use the MockitoJUnitRunner to run your test, then an ArgumentCaptor will be created for you, and there's no need to initialise it explicitly. If you do that, then you can also use the @Mock annotation to create your mock objects, that is, you don't need the mock static method any more. Your test class would then begin like this.

@RunWith(MockitoJUnitRunner.class)
public class WidgetStatusServletTest {

    @Captor private ArgumentCaptor<ArrayList<WidgetStatus>> captor;
    @Mock private DBController mockController;
    @Mock private HttpServletRequest mockRequest;
    @Mock private HttpServletResponse mockResponse;
    @Mock private Repository mockRepository;

This question addresses the issue of whether to use MockitoJUnitRunner, or an explicit call to MockitoAnnotations.initMocks.

Just a side note - your test methods will be much easier to read if you use variable names that clearly indicate that your objects are mocks, like I've done here. Otherwise, in a long test class with many different variables, it's easy to lose track of which variables reference mocks, and which reference "real" objects.

OTHER TIPS

Fields marked with annotations @Mock and @Captor (and also @Spy and @InjectMocks) will be initialized automatically by Mockito, but only if you call MockitoAnnotations.initMocks(this) in the test class.

You can do this yourself in setUp (JUnit3) or a @Before method (JUnit4), or annotate your test case with @RunWith(MockitoJUnitRunner.class) to get automatic initialization and valiidation.

It seems like the ArgumentCaptor is not getting initialized.

When using mockito annotations, they are initialized by either

  • Adding the MockitoAnnotations.initMocks(this); line in the setup method

     @Before
     public void setUp() throws Exception {
    
         MockitoAnnotations.initMocks(this);
     }
    
  • Or by adding @RunWith(MockitoJUnitRunner.class) annotation to the TestClass.

You did not initialize your captor object, therefore you cannot execute its capture() method. You must initialize an object before you are calling its methods.

If you are using Junit 5 jupitor and the annotations like @Spy @Captor and @Mock is not working for you then it is because the "RunWith" was removed, you now need to use the Extensions using the "@ExtendWith" Annotation.

So, using @ExtendWith(MockitoExtension.class) instead of @RunWith(MockitoJUnitRunner.class) will do the trick for you and the annotations will work as they are supposed to.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top