Chapter 3. Accelerating Mockito

"Any sufficiently advanced technology is indistinguishable from magic."

– Arthur C. Clarke

This chapter explores the advanced topics of the Mockito framework. Using Mockito's advanced features, we can stub out void methods, capture arguments passed to the stubbed methods and assert the argument values, verify the invocation order to check that the collaborators are accessed in proper order, spy a real object and set expectation on the spy object in the legacy code, and change mocking behavior.

The following topics are covered in this chapter:

  • Void methods
  • Annotations
  • Argument captor
  • Verifying an invocation order
  • Spying an object
  • Changing default Mockito settings
  • Resetting mock objects
  • Inline stubbing
  • Mock details

Learning advanced Mockito features

Chapter 2, Socializing with Mockito, explained the external dependencies and provided examples of basic Mockito features, such as stubbing method calls, throwing exceptions, matching arguments, verifying method invocations, and answering method calls.

Mockito provides a fluent API for mocking Java objects. It offers a collection of advanced features for advanced users. This section deals with the advanced Mockito features and answers several questions, such as how to change the Mockito settings to return smart null values instead of default return types, how to reset a mock object to clear all previous information, how to determine whether an object is a spy or a mock, and how to capture arguments passed to a mock object and verify the values.

The following sections cover the advanced Mockito APIs.

Working with void methods

Unit testing void methods is difficult. Conventional unit tests prepare data, pass values to a method, and then assert the return type to verify the behavior of the code. But when a method doesn't return a value but only changes the internal state of the object under test, it becomes difficult to decide what to assert. Conventional unit tests work with direct input and output, but void methods need to work with indirect output.

In this section, we'll examine a legacy servlet code and write unit test for the legacy code. To unit test a servlet code, you need the Servlet-apiXX.jar, JUnit JAR file, and the Mockito JAR file. To download servlet-api.<version number>.jar, you can visit the Oracle URL at http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-eeplat-419426.html, and we already have the JUnit and Mockito JAR files. On the other hand, you can download the code and associated JAR files for this chapter from the Packt website.

The following servlet code acts as a front controller. It intercepts all the web requests and delegates these requests to appropriate resources. The DemoController servlet extends from HttpServlet and has a dependency on a LoginController class. The constructor creates an instance of LoginController, as shown in the following code:

@WebServlet("/DemoController")
public class DemoController extends HttpServlet {
  private LoginController loginController;
  public DemoController() {
    loginController = new LoginController( new LDAPManagerImpl());
  }
}

The doPost() and doGet() methods are inherited from HttpServlet. The doPost() method intercepts the HTTP POST requests, and delegates calls to the doGet() method.

  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    doGet(request, response);
  }

The doGet() method intercepts all the HTTP GET requests, and depending on the request context URL, it routes the requests to appropriate handlers. Initially, the login.jsp page is opened for user login. On submission of the Login form, the /logon.do action is taken. The loginController class handles the /logon.do request, and all other requests are routed to the error page. The following is the body of the doGet() method:

  protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
    String urlContext = req.getServletPath();
    if(urlContext.equals("/")) {
      req.getRequestDispatcher("login.jsp").forward(req, res);
    }else if(urlContext.equals("/logon.do")) {
      loginController.process(req, res);
    }else {
      req.setAttribute("error", "Invalid request path '"+urlContext+"'");
      req.getRequestDispatcher("error.jsp").forward(req, res);
    }
  }

The LoginController class has a dependency on LDAPManager for user validation. This class handles the login request, retrieves the username and encrypted password from the HTTP request, and asks the LDAPManager to validate whether the user exists or not. The following is the LoginController class:

public class LoginController {
  private final LDAPManager ldapManager;

  public LoginController(LDAPManager ldapMngr) {
    this.ldapManager = ldapMngr;
  }
}

The process() method delegates user validation to LDAPManager and if the user is valid, then it creates a new session, puts the user information to the session, and routes the user to the home page. However, if the username or password is invalid, it forwards the request back to the login page.

  public void process(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
    String userName = req.getParameter("userName");
    String encrypterPassword = req.getParameter
("encrypterPassword");
    if (ldapManager.isValidUser(userName, encrypterPassword)) {
      req.getSession(true).setAttribute("user", userName);
      req.getRequestDispatcher("home.jsp").forward(req, res);
    } else {
      req.setAttribute("error", "Invalid user name or password");
      req.getRequestDispatcher("login.jsp").forward(req, res);
    }
  }

The process() method doesn't return any value, but validates user login, and on successful login, it routes the user to the home page. How can we unit test this behavior? We can verify that the isValidUser() method of LDAPManager is invoked, then check that the username is put in the session, and confirm that the request is dispatched to the home.jsp page.

We learned about the mocking object and verifying method invocation using the verify() method in Chapter 2, Socializing with Mockito. Here, we'll create a mock HttpServletRequest, HttpServletResponse, and an LDAPManager and verify that the actions are taken. We'll stub the isValidUser method of LDAPManager to return true to unit test the successful user login and return false to unit test the invalid login scenario. The following is the JUnit setup for the LoginController class:

package com.packt.mockito.advanced.voidmethods;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class LoginControllerTest {
  private LoginController controller;
  private @Mock HttpServletRequest request;
  private @Mock HttpServletResponse response;
  private @Mock LDAPManager ldapManager;
  @Before
  public void beforeEveryTest(){
    MockitoAnnotations.initMocks(this);
      controller = new LoginController(ldapManager);
  }

  @Test
  public void when_valid_user_credentials_for_login_Then_routes_to_home_page(){
  }
  @Test
  public void when_invalid_user_credentials_Then_routes_to_login_page(){
  }
}

Mock objects are instantiated by the MockitoAnnotations.initMocks(this) instance in the beforeEveryTest method. Two empty test methods are created for unit testing the valid and invalid login, and the sanity checking of the mock objects creation. We'll start with the happy path. Modify the when_valid_user_credentials_for_login_Then_routes_to_home_page() test, and then we'll modify the when_invalid_user_credentials_Then_routes_to_login_page test.

After successful login, the process() method creates a user session, puts the user information to the session, and then dispatches the request. Hence, for this, we need to create a mock HttpSession object and a RequestDispatcher object:

  @Mock HttpSession session;
  @Mock RequestDispatcher dispatcher;

We modify the happy path test to verify successful login. Happy path unit tests can verify the most obvious things, such as when a valid user ID and password is passed, the user can login; but when we test complicated business conditions, such as an invalid password or an expired password, we call it the alternate path or sad path. The following is the modified test:

@Test
public void when_valid_user_credentials_for_login_Then_routes_to_home_page() throws Exception{
  verify(ldapManager).isValidUser(anyString(),anyStrin());
  verify(request).getSession(true);
  verify(session).setAttribute(anyString(), anyString());
  verify(request).getRequestDispatcher(eq("home.jsp"));
  verify(dispatcher).forward(request, response);
}

We are verifying a successful login that requires a user session to be created, a session attribute to be set, a request dispatcher object to be created for the home page ("home.jsp"), and the request dispatcher to be forwarded to the home page. The JUnit test verifies that things are set up and executed sequentially. Similarly, modify the other test to unit test the invalid login. The following is the modified test:

@Test
public void when_invalid_user_credentials_Then_routes_to_login_page() throws Exception{
  verify(request).getRequestDispatcher(eq("login.jsp"));
  verify(dispatcher).forward(request, response);
}

The following output is shown in the Eclipse JUnit runner when we run the unit tests:

Working with void methods

We need to invoke the process() method and modify the first test to stub LDAPManager to return true in order to simulate a successful login. The following is the modified test:

@Test
public void when_valid_user_credentials_for_login_Then_routes_to_home_page() throws Exception{
  when(ldapManager.isValidUser(anyString(), anyString())).thenReturn(true);
  when(request.getSession(true)).thenReturn(session);
  when(request.getRequestDispatcher(anyString())).thenReturn(dispatcher);
  when(request.getParameter(anyString())).thenReturn("user","pwd");

  controller.process(request, response);

  verify(request).getSession(true);
  verify(session).setAttribute(anyString(), anyString());
  verify(request).getRequestDispatcher(eq("home.jsp"));
  verify(dispatcher).forward(request, response);
}

The isValidUser method of the ldapManager is stubbed to return true, request.getSession() is stubbed to return a mock HttpSession object, request.getRequestDispatcher() is stubbed to return a mock RequestDispatcher, and finally, the request.getParameter method is stubbed to return "user" and then "pwd". When we run the tests again, the first test passes! The following is the test output:

Working with void methods

We must modify the second test to stub the isValidUser method to return false, stub the request.getRequestDispatcher() to return a mock RequestDispatcher, and finally, stub the request.getParameter method to return "user" and then "pwd". The following is the modified test:

@Test
public void when_invalid_user_credentials_Then_routes_to  _login_page() throws Exception{
  when(ldapManager.isValidUser(anyString(), anyString())).thenReturn(false);
  when(request.getRequestDispatcher(anyString())).thenReturn(dispatcher);
  when(request.getParameter(anyString())).thenReturn("user","pwd");

  controller.process(request, response);

  verify(request).getRequestDispatcher(eq("login.jsp"));
  verify(dispatcher).forward(request, response);
}

When we run the tests, we get a green bar as shown in the following screenshot:

Working with void methods

We learned how to unit test void methods. Revisit the tests; you will find duplicate code in the test methods, such as stubbing the getParameter() method or stubbing the getRequestDispatcher() method. You can move the stubbing calls to the beforeEveryTest method to clean the test code.

The following section explores the concept of exception handling for void methods.

Throwing exceptions from void methods

In the preceding example, the LoginController class calls the LDAPManager for user validation. The web application fails if the LDAPManager throws an exception. The DemoController servlet is the gateway; it should handle any unwanted exceptions and show a proper error message to the user. We have to find a mechanism to handle exceptions.

We'll create a unit test for the DemoController servlet. To recreate an exceptional condition, we have to stub the LoginController class to throw an exception, but the problem is the DemoController constructor. The constructor instantiates the LoginController class, so we cannot mock the controller. We can refactor the DemoController constructor to pass a mock instant of the LoginController class. There are several ways to achieve this; for now, we'll add a constructor to pass the mocked LoginController class. We cannot remove the default constructor, otherwise the servlet container will fail to instantiate the servlet. Servlets run in a container and the container maintains the servlet's lifecycle. The container invokes the default constructor to instantiate a servlet instance. If we remove the default constructor, the container will fail to create the servlet. The following is the modified code:

@WebServlet("/DemoController")
public class DemoController extends HttpServlet {
  private final LoginController loginController;

  public DemoController(LoginController loginController) {
    this.loginController = loginController;
  }
  public DemoController() {
    loginController = new LoginController(new LDAPManagerImpl()) ;
  }
}

The following is the empty unit test for the DemoController constructor:

public class DemoControllerTest {
  DemoController controller;
  @Mock LoginController loginController;

  @Before  public void beforeEveryTest(){
    MockitoAnnotations.initMocks(this);
    controller = new DemoController(loginController);
  }
}

We'll modify the code to handle the exceptions and route the request to an error page. After catching the exception, the servlet will dispatch the request to the error page. So we need to create a mock HttpServletRequest object, an HttpServletResponse object, and a RequestDispatcher object:

  @Mock HttpServletRequest request;
  @Mock HttpServletResponse response;
  @Mock RequestDispatcher dispatcher;

Add the following test to simulate the scenario:

@Test
public void when_subsystem_throws_exception_Then_routes_to_error_page_() throws Exception {

  verify(request).getRequestDispatcher(eq("error.jsp"));
  verify(dispatcher).forward(request, response);
}

We are verifying the request dispatcher creation for the error.jsp error page. The LoginController class needs to throw an exception. The Mockito convention for throwing an exception from a void method is as follows:

  doThrow(exception).when(mockObject).someVoidMethod();

We'll modify the test to stub the process() method in order to throw an exception. The following is the modified test code:

@Test
public void when_subsystem_throws_exception_Then_routes_to_error_page_() throws Exception {
  doThrow(new IllegalStateException("LDAP error")).when(loginController).process(request, response);
  when(request.getServletPath()).thenReturn("/logon.do");
  when(request.getRequestDispatcher(anyString())).thenReturn(dispatcher);
  controller.doGet(request, response);
  verify(request).getRequestDispatcher(eq("error.jsp"));
  verify(dispatcher).forward(request, response);
}

When we run the test, it fails for an unhandled exception as exception handling has not been done yet. The following is the JUnit output:

Throwing exceptions from void methods

Modify the DemoController constructor to handle exceptions. The following is the modified code:

protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
  try {
    String urlContext = req.getServletPath();
    if (urlContext.equals("/")) {
      req.getRequestDispatcher("login.jsp").forward(req, res);
    } else if (urlContext.equals("/logon.do")) {
      loginController.process(req, res);
    } else {
      req.setAttribute("error", "Invalid request path '" + urlContext + "'");
      req.getRequestDispatcher("error.jsp").forward(req, res);
    }
  } catch (Exception ex) {
    req.setAttribute("error", ex.getMessage());
    req.getRequestDispatcher("error.jsp").forward(req, res);
  }

}

Rerunning the test passes execution; the following is the test output:

Throwing exceptions from void methods

Working with void method callbacks

An external code dependency may process data in a void method, for example, it may send an e-mail or update a database row. We can easily stub a void method by mocking the dependency, but at times, void methods may change the input argument object's attribute, for example, it may set the error code of an Error object passed in as an argument, and we may use the modified value in our calculation. In this scenario, if we stub the void method, it doesn't help us to modify or add the stubbed method's argument attribute. As a result, our test might either fail or some portion of the code might remain untested.

Consider the exception handling code for DemoController. It retrieves the error message of the exception and passes the message to the end users, as the message might not be useful to the business users; it doesn't make any sense to us if we see a NullPointerException error while booking a movie ticket. Instead of passing the raw business exception to the user, the system should analyze the error message, form a useful error message, and pass a meaningful message to the end user.

We'll modify the DemoController code to analyze the StackTrace method, retrieve an error message code for the trace, look up the code for a meaningful error message, and pass the message to the user. We'll create an Error object with an array of StackTraceElement and an errorCode string. The following is the code:

public class Error {
  private StackTraceElement[] trace;
  private String errorCode;
  //Getters and setters are ignored for brevity 
}

An ErrorHandler interface takes the Error object, maps the StackTraceElements method to an errorCode string, and sets the code back to the Error object. The following is the code body:

public interface ErrorHandler {

  void mapTo(Error error);
}

The MessageRepository interface looks up the error code and retrieves a meaningful message from the database. The following is the MessageRepository class:

public interface MessageRepository {
  String lookUp(String... errorCode);
}

The following modified DemoController code invokes the ErrorHandler and MessageRepository interface to get a meaningful message, and passes the message to the user.

  } catch (Exception ex) {
    String errorMsg = ex.getMessage();
    Error errorDto = new Error();
    errorDto.setTrace(ex.getStackTrace());
    errorHandler.mapTo(errorDto);

    if(errorDto.getErrorCode() != null){
      errorMsg = messageRepository.lookUp (errorDto.getErrorCode());
    }
    req.setAttribute("error", errorMsg);
    req.getRequestDispatcher("error.jsp").forward(req, res);
  }

We ignored the rest of the method and dependencies for brevity. You can download the code for details. The mapTo method takes an Error object and populates the errorCode string of the Error object. If no matching errorCode string is found, the errorCode remains as it is. If the errorCode string is found, the errorCode string is passed to messageRepository for an error message lookup.

When we mock the dependencies (errorHandler and messageRepository) and rerun the tests, some portion remains untested. The following is the screenshot of the untested code:

Working with void method callbacks

We should modify the Error object from the void mapTo method to unit test the untested line. The mapTo method looks up the database to map a StackTrace method with an error code, so we must mock out the database call and stub the void method. The following are reasons behind mocking the database call, and you must configure your tests to adhere to these principles:

  • Fast execution: Tests should be executed extremely fast, so that they can provide quick feedback. Would you care to wait for a build system that takes 2 hours to finish execution? This means if a test fails, you have to wait for 2 hours to verify your fix.
  • Tests should be reliable: Tests should fail if the production code is broken. Your tests will be considered unreliable in the following situations:
    • You break the production logic, but the tests pass
    • You don't touch the production code, but your tests still fail
  • In-memory data dependent: Tests should depend on in-memory data rather than pulling data from an external source, for instance, accessing the database for data can fail a test if the expected data is not present in the database for any reason, such as, if someone has deleted the data.

However, if we stub the void method, how can we set the errorCode string to the Error object? Also, we cannot directly set the Error object attributes as the object is created inside the catch block.

The resolution is Mockito's doAnswer() method. The doAnswer() method can intercept the void method call and access the void method arguments and the mock object. So, we can create our callback Answer implementation, access the Error object passed as an argument, and set an errorCode string to it. The following is the syntax for doAnswer():

doAnswer(answer).when(mock).someVoidMethod();

We'll create an anonymous Answer object , access the Error object, and set the errorCode string. The following is the code:

@Test
public void when_subsystem_throws_any_exception_Then_finds_error_message_and_routes_to_error_page_() throws Exception {
  doThrow(new IllegalStateException("LDAP error")).when(loginController).process(request, response);
  doAnswer(new Answer<Object>() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
      Error err = (Error)invocation.getArguments()[0];
      err.setErrorCode("123");
      return err;
    }
  }
  ).when(errorHandler).mapTo(isA(Error.class));

  when(request.getServletPath()).thenReturn("/logon.do");
  when(request.getRequestDispatcher(anyString())).thenReturn(dispatcher);

  controller.doGet(request, response);

  verify(request).getRequestDispatcher(eq("error.jsp"));
  verify(dispatcher).forward(request, response);
}

The preceding change covers the untested lines. The following screenshot shows the test coverage output:

Working with void method callbacks

Learning doCallRealMethod and doNothing

In this section, we'll explore two methods, namely, doNothing and doCallRealMethod.

The doNothing() method does nothing. By default, when we create a mock object and call a void method on that mock object, the void method does not do anything, or rather, it is stubbed by default, but still, we stub void methods using doNothing() for void method chaining. If you need consecutive calls on a void method, the first call to throw an error, the next call to do nothing, and then the call after that to perform some logic using doAnswer(), then follow the ensuing syntax:

  doThrow(new RuntimeException()).
  doNothing().
  doAnswer(someAnswer).
  when(mock).someVoidMethod();

The doCallRealMethod() method is used when you want to call the real implementation of a method on a mock object. The following is the syntax:

doCallRealMethod().when(mock).someVoidMethod();

Exploring doReturn

The doReturn() method is like thenReturn(), but this is used only when when(mock).thenReturn(return) cannot be used. The when().thenReturn() method is more readable than doReturn(). Also, doReturn() is not type safe. The thenReturn method checks method return types and raises a compilation error if an unsafe type is passed. You can use doReturn() when working with spy objects. Here is the syntax for using the doReturn() test:

doReturn(value).when(mock).method(argument);

The following code snippet provides an example of unsafe usage of doReturn:

@Test
public void when_do_return_is_not_safe() throws Exception {
  when(request.getServletPath()).thenReturn("/logon.do");
  assertEquals("/logon.do", request.getServletPath());

  doReturn(1.111d).when(request.getServletPath());
  request.getServletPath();
}

The request.getServletPath() method returns a string value. If we try to stub the request.getServletPath() method with a double using thenReturn, the Java compiler will complain about the return type; but if we use doReturn and return a double value, the test fails at runtime. So doReturn has two drawbacks; it is unreadable and error prone. The following is the test output:

Exploring doReturn

The following screenshot shows the failure trace:

Exploring doReturn

The doReturn method becomes handy with spy objects. We'll explore doReturn in the spy section.

Verifying arguments using ArgumentCaptor

An ArgumentCaptor object verifies the arguments passed to a stubbed method. Sometimes, we create an object in our code under test and then pass it to a method on a mocked dependency, but never return it. Argument captors let us directly access these values provided to our mocks in order to examine them more closely. An ArgumentCaptor object provides an API to test the computed value.

In our exception handling code, we create an Error object, set exception trace to the object, invoke the ErrorHandler interface to map the Error object to an errorCode string, and finally, call the MessageRepository class to return a meaningful error message for the errorCode string. An ArgumentCaptor can return to us the argument details passed to a stubbed method.

Mockito verifies argument values in natural Java style by using the equals() method. This is also the recommended way for matching arguments because it makes tests clean and simple. In some situations though, it is helpful to assert on certain arguments after the actual verification.

An ArgumentCaptor object is defined as follows:

ArgumentCaptor<T> argCaptor= ArgumentCaptor.forClass(T.class);

Where T is the type of argument, such as a string or a user-defined class.

The following syntax is used to capture arguments:

verify(mockObject).methodA(argCaptor.capture());

If an ArgumentCaptor object captures arguments for multiple invocations, the captured values can be retrieved by calling the getAllValues() method. The getAllValues() method returns List<T> and the getValue() method returns T, which is the last method invocation result. Here, T is the type of argument class, such as an integer or any Java class type.

The following code uses an ArgumentCaptor to verify the argument passed into the lookUp method.

ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(repository).lookUp(captor.capture());
assertEquals("123", captor.getValue());

Working with generic collection arguments

The following example demonstrates how to capture collection arguments. Create an interface and add a method to accept a list of strings. The following is the code:

interface Service{
  void call(List<String> args);
}

Try to create an ArgumentCapture for the list of strings. You cannot create a class for List<String>.class, so you can try to use List.class. The following screenshot shows you the Java compilation error while converting List.class to List<String>:

Working with generic collection arguments

The following code snippet creates List.class and casts it to Class<List<String>>, and passes it to ArgumentCaptor. This will give you warnings about unsafe casts; you can suppress the warning by annotating the construct with @SuppressWarnings("unchecked"):

@Test
public void when_captures_collections() throws Exception {
  Class<List<String>> listClass = (Class<List<String>>)(Class)List.class;
  ArgumentCaptor<List<String>> captor = ArgumentCaptor.forClass(listClass);
}

The following test provides an example of such a use. Here, service is a mocked implementation of the Service interface:

@Test public void when_captures_collections(){
  Class<List<String>> listClass = (Class<List<String>>)(Class)List.class;
  ArgumentCaptor<List<String>> captor = ArgumentCaptor.forClass(listClass);
  service.call(Arrays.asList("a","b"));
  verify(service).call(captor.capture());
  assertTrue(captor.getValue().
  containsAll(Arrays.asList("a","b")));
}

Working with variable arguments and arrays

The following example shows you how to capture an argument of type arrays or var-args (T... t).

Modify the MessageRepository class to accept variable arguments of strings as errorCodes. The following is the modified code:

public interface MessageRepository {
  String lookUp(String... errorCode);
}

Create a test to pass an array to the lookUp method and capture values. The following is the code snippet:

@Test
public void when_capturing_variable_args() throws Exception {
  String[] errorCodes = {"a","b","c"};

  ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
  repository.lookUp(errorCodes);
  verify(repository).lookUp(captor.capture(),captor.capture(),captor.capture());
  assertTrue(captor.getAllValues().containsAll(Arrays.asList(errorCodes)));
}

The following Mockito URL has the fix for the variable argument capture:

https://github.com/mockito/mockito/commit/e43a958833df5aa46f54d7cd83b1c17fa19cc5dc

ArgumentCaptor is modified in a default branch to capture variable arguments. The following is the code snippet:

verify(messageRepository).lookUp(argumentCaptor.captureVararg());

Note

Note that this fix is not available in the latest Mockito build 1.9.5.

Verifying an invocation order

Mockito facilitates verification if interactions with a mock were performed in a given order using the InOrder API. It allows us to create an InOrder of mocks and verify the call order of all the calls of all the mocks.

InOrder is created with mock object using the following syntax:

InOrder inOrder=inOrder(mock1,mock2,...mockN);

Method invocation order is checked using the following syntax:

inOrder.verify(mock1).methodCall1();
inOrder.verify(mock2).methodCall2();

If methodCall2() of mock2 is invoked before methodCall1() of mock1, the test fails. The following test verifies the test order:

@Test
public void when_inorder() throws Exception {
  request.getServletPath();
  service.call(Arrays.asList("a","b"));
  InOrder inOrder=inOrder(request,service);
  inOrder.verify(service).call(anyList());
  inOrder.verify(request).getServletPath();
}

The test verifies that the call() method is invoked before the getServletPath() method, but the methods were invoked in reverse order, so the test will fail. The following screenshot demonstrates the error:

Verifying an invocation order

Reordering the verification sequence in the following manner fixes the test:

@Test public void when_inorder() throws Exception {
  request.getServletPath();
  service.call(Arrays.asList("a","b"));
  InOrder inOrder=inOrder(request,service);
  inOrder.verify(request).getServletPath();
  inOrder.verify(service).call(anyList());
}

Spying objects

A Mockito spy allows us to use real objects instead of mocks by replacing some of the methods with stubbed ones. This behavior allows us to test the legacy code. The spy is useful for legacy code as you cannot invoke a few testing impediment methods from your code under test, and also, you cannot mock a class that needs to be tested. A spy can stub these testing impediments without mocking the code under test. A spy can stub the nontestable methods so that other methods can be tested easily. You can also use spies without doing any stubbing and just use them to verify interactions between two totally real objects.

Once an expectation is set for a method on a spy object, the spy object no longer returns the original value. It starts returning the stubbed value, but still exhibits the original behavior for the other methods that are not stubbed.

Mockito can create a spy for a real object. Unlike stubbing, when we use the spy object, real methods are called (unless a method was stubbed).

Spy is also known as partial mock. The following is the declaration of spy:

SomeClass realObject = new RealImplemenation();
SomeClass spyObject = spy(realObject);

The following is a self-explanatory example of spy:

@Test
public void when_spying_real_objects() throws Exception {
  Error error = new Error();
  error.setErrorCode("Q123");
  Error spyError = spy(error);
  //call real method from  spy
  assertEquals("Q123", spyError.getErrorCode());

  //Changing value using spy
  spyError.setErrorCode(null);

 //verify spy has the changed value
  assertEquals(null, spyError.getErrorCode());

  //Stubbing method
  when(spyError.getErrorCode()).thenReturn("E456");

  //Changing value using spy
  spyError.setErrorCode(null);

  //Stubbed method value E456 is returned NOT NULL
  assertNotEquals(null, spyError.getErrorCode());

  //Stubbed method value E456
  assertEquals("E456", spyError.getErrorCode());
}

Spying real objects and calling real methods on a spy object has side effects; to immunize this side effect, use doReturn() instead of thenReturn().

The following code describes the side effect of spying and calling thenReturn():

@Test
public void when_doReturn_fails() throws Exception {
  List<String> list = new ArrayList<String>();
  List<String> spy = spy(list);
  //impossible the real list.get(0) is called and fails
  //with IndexOutofBoundsException, as the list is empty
  when(spy.get(0)).thenReturn("not reachable");
}

The spy object calls a real method when trying to stub get(index), and unlike the mock objects, the real method was called and it failed with an ArrayIndexOutOfBounds error. The following screenshot displays the failure message:

Spying objects

This failure can be protected using doReturn(), as shown is the following code:

@Test public void when_doReturn_fails() throws Exception {
  List<String> list = new ArrayList<String>();
  List<String> spy = spy(list);

  //doReturn fixed the issue
  doReturn("now reachable").when(spy).get(0);
  assertEquals("now reachable", spy.get(0));
}

Exploring Mockito annotations

We learned that Mockito supports the @Mock annotation for mocking. Like @Mock, Mockito offers three useful annotations, namely, @Spy, @Captor, and @InjectMocks:

  • @Captor: This simplifies the creation of ArgumentCaptor, and this is useful when the argument to capture is a horrible generic class
  • @Spy: This creates the spy of a given object; use it instead of spy(Object)
  • @InjectMocks: It injects mock or spy fields into tested objects automatically using constructor injection, setter injection, or field injection

The following example demonstrates the @captor annotation:

@RunWith(MockitoJUnitRunner.class)
public class AnnotationTest {

  @Captor
  ArgumentCaptor<List<String>> captor;
  @Mock Service service;

  @Test
  public void when_captor_annotation_is_used() {
    service.call(Arrays.asList("a","b"));
    verify(service).call(captor.capture());
    assertTrue(captor.getValue().containsAll(Arrays.asList("a","b")));
  }
}

The annotation creates the ArgumentCaptor object, and we don't need to typecast it to Class<List<String>>.

The following example demonstrates the use of the @spy annotation:

@RunWith(MockitoJUnitRunner.class)
public class SpyAnnotationTest {

  @Spy
  ErrorHandlerImpl errorHandler;

  @Test
  public void when_spy_annotation_is_used() throws Exception {
    assertNotNull(errorHandler);
  }
}

A Spy object of ErrorHandlerImpl is created automatically for errorHandler. You cannot create a spy for an interface. The following error message pops up when we try to create a spy for the ErrorHandler interface:

  @Spy
  ErrorHandler errorHandler;

The following screenshot displays the error message:

Exploring Mockito annotations

The following example demonstrates the use of the @InjectMocks annotation. Here, we'll create a @spy annotation and two @mocks annotations. The @InjectMocks annotation sets the mocks and spy to the real object as a constructor injection.

@RunWith(MockitoJUnitRunner.class)
public class InjectMocksAnnotationTest {

  @Mock LoginController loginController;
  @Mock MessageRepository repository;
  @Spy ErrorHandlerImpl errorHandler;

  @InjectMocks
  DemoController controller;

  @Mock HttpServletRequest request;
  @Mock HttpServletResponse response;
  @Mock RequestDispatcher dispatcher;

  @Test
  public void when_mocks_are_injected() throws Exception {
    when(request.getServletPath()).thenReturn("/");
    when(request.getRequestDispatcher(anyString())).thenReturn(dispatcher);
    controller.doGet(request, response);
    verify(request).getRequestDispatcher(eq("login.jsp"));
  }
}

The DemoController constructor depends on three classes; the preceding example creates the mock and spy objects and injects them to the DemoController constructor.

Changing the default Mockito settings

We learned that nonstubbed methods of a mock object return default values, such as Null for an object and false for a Boolean. However, Mockito allows us to change the default settings to return other nondefault values; these are basically preconfigured answers. The following are settings that are allowed:

  • RETURNS_DEFAULTS: This is the default setting that returns null for an object, false for a Boolean, and so on.
  • RETURNS_SMART_NULLS: This returns smart nulls, which are stubs that act like nulls (in that they throw exceptions if you try and call stub.anyMethod()), but throw exceptions that are much more useful than normal NullPointerExceptions by giving you information on which call they came from and where.
  • RETURNS_MOCKS: This returns mock for objects and default value for primitives. If the object cannot be mocked (such as a final class), a Null value is returned.
  • RETURNS_DEEP_STUBS: This returns a deep stub. This is really important for legacy code where we need to stub the method chaining, for example, when Foo calls getBar().getTar().getName(). Deep stubbing allows Foo to directly stub the getName() method to return a value. Otherwise, we have to stub Foo's getBar method to return a mock Bar object, stub the bar's getTar() method to return a mock Tar object, and finally, stub the Tar's getName method to return a value.
  • CALLS_REAL_METHODS: This calls the corresponding method from the real implementation of the mocked class.

The following example overrides the default Mockito settings and uses different return types. Suppose we have the following classes:

class Foo {
  Bar bar;
  //Getter and setter
}
class Bar {
  Tar tar;
  //Getter and setter
}
class Tar {
  private String name;
  //Getter and setter
}

The following test case uses the RETURNS_DEFAULTS setting to return a NULL Bar object:

@Test
public void when_default_settings() throws Exception {

  Foo fooWithReturnDefault = Mockito.mock(Foo.class, Mockito.RETURNS_DEFAULTS);
  // default null is returned
  assertNull(fooWithReturnDefault.getBar());
}

The following test case uses the RETURNS_SMART_NULLS setting to return a smart NULL object:

@Test
public void when_changing_default_settings_to_return_smartNULLS(){

  Foo fooWithSmartNull = Mockito.mock(Foo.class, Mockito.RETURNS_SMART_NULLS);
  // a smart null is returned
  assertNotNull(fooWithSmartNull.getBar());
  System.out.println("fooWithSmartNull.getBar() =" + fooWithSmartNull.getBar());
}

The following is the System.out output:

fooWithSmartNull.getBar() =SmartNull returned by this unstubbed method call on a mock:foo.getBar();

The following test case uses the RETURNS_MOCKS setting to return a mock object hierarchy:

@Test
public void when_changing_default_settings_to_return_mocks() {

  Foo fooWithReturnsMocks = Mockito.mock(Foo.class, Mockito.RETURNS_MOCKS);
  // a mock is returned
  Bar mockBar = fooWithReturnsMocks.getBar();
  assertNotNull(mockBar);
  assertNotNull(mockBar.getTar());
  assertNotNull(mockBar.getTar().getName());
  System.out.println("fooWithReturnsMocks.getBar()=" + mockBar);
  System.out.println("fooWithReturnsMocks.getBar().getTar().getName()={" + mockBar.getTar().getName()+"}");
}

The RETURNS_MOCKS setting populates the Foo object with a mocked Bar object. A mocked Bar object has a mocked Tar object and the mocked Tar object has an empty mocked string name. The following is the output:

fooWithReturnsMocks.getBar()=Mock for Bar, hashCode: 1620275837
fooWithReturnsMocks.getBar().getTar().getName()={}

The following test case uses the RETURNS_DEEP_STUBS setting to return a deep-stubbed object hierarchy:

@Test
public void when_returns_deep_stub() throws Exception {
  Foo fooWithDeepStub = Mockito.mock(Foo.class, Mockito.RETURNS_DEEP_STUBS);
  when(fooWithDeepStub.getBar().getTar().getName()).thenReturn("Deep Stub");
  // a deep stubbed mock is returned
  System.out.println("fooWithDeepStub.getBar().getTar().getName()="+ fooWithDeepStub.getBar().getTar().getName());
  assertNotNull(fooWithDeepStub.getBar().getTar().getName());
}

The RETURNS_DEEP_STUBS setting is very useful for legacy code. In the preceding example, we had to stub the getName() method of a Tar object, but to stub the method, we had to mock a series of other objects. Only when we used the RETURNS_DEEP_STUBS setting could the chaining of the method call stub the method and other objects.

The following is the print output:

fooWithDeepStub.getBar().getTar().getName()=Deep Stub

Resetting mock objects

A static method reset(Tâ€Ĥ ) enables the resetting of mock objects. The reset() method clears the stubs.

The following code snippet stubs the getName() method of a mocked Bar object. After resetting the getName() method, the stubbing gets cleared and starts returning the default NULL value.

@Test
public void when_resetting_mocks() throws Exception {
  Bar bar= Mockito.mock(Bar.class);
  when(bar.getName()).thenReturn("***");
  assertNotNull(bar.getName());
  reset(bar);
  //Bar is reset, the getName() stub is cleared
  assertNull(bar.getName());
}

Resetting mocks is not recommended as it's a sign that your test is probably doing too much, and you should probably just have another test with fresh mocks instead.

Working with inline stubbing

Mockito allows us to create mocks while stubbing it. Basically, it allows you to create a stub in one line of code. This can be helpful to keep the test code clean. For example, some stubs can be created and stubbed during field initialization in a test:

public class InlineStubbing {

  Bar bar =  when(mock(Bar.class).getTar()).thenReturn(new Tar()).getMock();
  @Test
  public void when_stubbing_inline() throws Exception {
    assertNotNull(bar);
    assertNotNull(bar.getTar());
  }

}

The bar object is stubbed and created at the same time. This is useful when the bar object is used in many test cases within the test class. The bar object should always return a Tar object.

Determining mock details

Sometimes, we need to determine whether an object is a mock or a spy. This situation can arise when an object uses the @injectMocks annotation; it can inject a spy or a mock object. We can find out the type using Mockito.mockingDetails. It can identify whether a particular object is a mock or a spy.

The following example demonstrates the Mockito.mockingDetails API.

The ServiceImpl class has two dependencies, namely, Dependency1 and Dependency2.

class Dependency1{
  
}
class Dependency2{
  
}

The following is the ServiceImpl class:

class ServiceImpl{
  private final Dependency1 dependency1;
  private final Dependency2 dependency2;
  public ServiceImpl(Dependency1 dependency1, Dependency2 dependency2) {
    this.dependency1 = dependency1;
    this.dependency2 = dependency2;
  }
  public Dependency1 getDependency1() {
    return dependency1;
  }
  public Dependency2 getDependency2() {
    return dependency2;
  }

}

The following test demonstrates the usage of mockingDetails:

import static org.mockito.Mockito.mockingDetails;

@RunWith(MockitoJUnitRunner.class)
public class MockDetailsTest {
  @Spy Dependency1 dep;
  @Mock Dependency1 dep1;
  @Mock Dependency2 dep2;
  @InjectMocks  ServiceImpl service;

  @Test public void when_determining_type() throws Exception {
    assertNotNull(service);
    assertTrue(mockingDetails(service.getDependency2()).isMock());
    assertTrue(mockingDetails(dep).isSpy());
  }
}

The Service object can be populated with a stub or a mock Dependency1. We verified that Dependency2 is a mock and dep1 is a spy. We can also verify service.getDependency1() to check whether a mock or a stub was injected.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.29.71