techiehub.in

Effective Unit Testing in Java

Overview

This article lists down (but not limited to), some common unit testing patterns and how to tackle them in Java. It assumes the use of Mockito java test framework for the demonstration purpose.

Full code could be found in github.

To bring the patterns to life, I have created five simple classes with minimal code. We will use this to write the corresponding test scenarios to bring the patterns to life. Below is a short description of these classes:

  • Order : POJO for Order
  • User : POJO for user
  • UserService : Service to manage user
  • UserUtil : Utility class for user
  • OrderService : Service to manage order

Testing Patterns

Mocking a static class

Pattern: When we have a utility class and we have to mock the methods of this class when invoked from another class.

In the below example, we are writing a test for createUser method in UserService class.

public int createUser() {
  boolean isUserIdValid = UserUtil.validateUserId(user.getUserId());
  if(isUserIdValid) {
      logger.info(“UserId provided is valid”);
      return USER_CREATE_OK;
  }
  return USER_CREATE_FAILED;
}

This method makes use of validateUserId method defined in UserUtil class. For unit testing createUser method, we would like to mock validateUserId method. We are creating a MockedStatic object of UserUtil to achieve the same. During this test run, validateUserId of UserUtil class  will always return the mocked value true.

@Test
public void testCreateUserUsingStaticMock() {
  try(MockedStatic<UserUtil> mockUserUtil = mockStatic(UserUtil.class)) {
      mockUserUtil.when(() -> validateUserId(anyString())).thenReturn(true);
      Assertions.assertEquals(UserService.USER_CREATE_OK, userService.createUser());
  }
}

Call Real Method from Static Mocked class

Pattern: When we have a mocked static class, but we would like to call some of the real methods of this static class.

In the below example, we are writing a test for validateUserId method defined in UserUtil class.

This method internally invokes checkNumOfCharsInUserId method which is also defined in the same class.

For writing this test, we want to call the real method validateUserId, but we would want to mock checkNumOfCharsInUserId.

Below code snippet shows that, we have to define mockUserUtil, with an additional parameter Answers.CALLS_REAL_METHOD which would call any real method, unless a mock is specified (i.e. delegates unstubbed calls to real implementation). Here, we have defined a mock for checkNumOfCharsInUserId which always returns 5 when the test is executed.

Note: There is a slight nuance in terms of how Mockito library has implemented CALLS_REAL_METHOD i.e. the output of the mocked method will be what is present in thenReturn, but the actual method is still invoked (ideally it should not).

@Test
public void testValidUserIdWithCallRealMethod() {
  try(MockedStatic<UserUtil> mockUserUtil = mockStatic(UserUtil.class, Answers.CALLS_REAL_METHODS)) {
      mockUserUtil.when(() -> checkNumOfCharsInUserId(anyString())).thenReturn(5);
      Assertions.assertTrue(UserUtil.validateUserId(“userId1234567890”));
  }
}

Call Real Method for Mock objects

Pattern: When we have mocked a class object, but we would like to call some of the real methods of this mocked instance.

In the below example, we are writing a test for createOrder method of OrderService. 

We have mocked the Order class and we are spying on UserService class.

When the createOrder method is called, internally it invokes isOrderAmended method of Order class. As we are using doCallRealMethod() on this method of the mocked instance of Order class, it will call the actual isOrderAmended method.

@Test
public void testCreateOrder_mockWithDoCallRealMethod() {
  mockOrder = mock(Order.class);
  Mockito.when(mockOrder.orderSize()).thenReturn(10);

  //Actual isOrderAmended method will be called when it is invoked from createOrder() method.
  doCallRealMethod().when(mockOrder).isOrderAmended();

  OrderService orderService = new OrderService(mockOrder, mockUser);
  UserService userService = orderService.getUserService();
  userServiceSpy = spy(userService);
  orderService.setUserService(userServiceSpy);
  doReturn(true).when(userServiceSpy).isUserCreated();
  //Use this when you want to avoid calling any public method returning void in the class you are spying
  doNothing().when(userServiceSpy).connectToDatabase();

  int actualResult = orderService.createOrder();

  verify(userServiceSpy, times(1)).isUserCreated();
  Assertions.assertEquals(OrderService.ORDER_CREATE_OK, actualResult);
}

Spy with Verify Method Invoked

Pattern: When we are spying on a particular instance of an class, we would like to validate the number of times a particular method of this instance is invoked.

In the below example, we are writing a test for createOrder method of OrderService. 

We have mocked the Order class and we are spying on UserService class.

Why we use Spy?

There are certain situations where you would like instrument an object instance post the initialization. In such scenarios, we use spy instead of mock.

In this example, userService is instantiated as part of the initialization of OrderService. Then we instrument the method of userService class to check how many times it got invoked when createOrder method is called.

@Test
public void testCreateOrder_spyWithVerify() {
  mockOrder = mock(Order.class);
  Mockito.when(mockOrder.orderSize()).thenReturn(10);

  OrderService orderService = new OrderService(mockOrder, mockUser);
  UserService userService = orderService.getUserService();

  //Spy is used when you want to instrument/monitor an already instantiated class as opposed to mocks where you inject the mocks upfront for that class.

  userServiceSpy = spy(userService);
  orderService.setUserService(userServiceSpy);

  int actualResult = orderService.createOrder();

  //Use this when you want to count the number of times any public method is invoked in the class you are spying
  verify(userServiceSpy, times(1)).isUserCreated();
  Assertions.assertEquals(OrderService.ORDER_CREATE_FAILED, actualResult);

}

Spy with doNothing

Patterns: When we are spying on a particular instance of a class and would like to avoid invoking the actual public method of a class returning void.

In the below example, we are writing a test for createOrder method of OrderService. 

We have mocked the Order class and we are spying on UserService class.

When the createOrder method is called, internally it invokes connectToDatabase() method of userService class. As we are using doNothing() on this method of the spy instance of userService class, it will skip calling this method altogether.

@Test
public void testCreateOrder_spyWithDoNothing() {
  mockOrder = mock(Order.class);
  Mockito.when(mockOrder.orderSize()).thenReturn(10);

  OrderService orderService = new OrderService(mockOrder, mockUser);
  UserService userService = orderService.getUserService();
  userServiceSpy = spy(userService);
  orderService.setUserService(userServiceSpy);

  //Use this when you want to avoid calling any public method returning void in the class you are spying
  doNothing().when(userServiceSpy).connectToDatabase();

  int actualResult = orderService.createOrder();

  verify(userServiceSpy, times(1)).isUserCreated();
  Assertions.assertEquals(OrderService.ORDER_CREATE_FAILED, actualResult);

}

Spy with doThrow

Pattern: When we are spying on a particular instance of a class and would like to simulate an exception scenario when a public method of this class is instantiated.

In the below example, we are writing a test for createOrder method of OrderService.

We have mocked the Order class and we are spying on UserService class.

When the createOrder method is called, internally it invokes connectToDatabase() method of userService class. As we are using doThrow() on this method of the spy instance of userService class to throw Runtime exception , it’s used to test the situation on how createOrder handles this Runtime exception.

@Test
public void testCreateOrder_spyWithDoThrowException() {
  mockOrder = mock(Order.class);
  Mockito.when(mockOrder.orderSize()).thenReturn(10);

  OrderService orderService = new OrderService(mockOrder, mockUser);
  UserService userService = orderService.getUserService();
  userServiceSpy = spy(userService);
  orderService.setUserService(userServiceSpy);

  //Use this when you want to simulate exception scenario by calling any public method in the class you are spying
  doThrow(new RuntimeException(“Ending the while loop intentionally to finish the test”)).when(userServiceSpy).connectToDatabase();

  Assertions.assertThrows(RuntimeException.class, orderService::createOrder);
}

Spy with doAnswer

Pattern: When we are spying on a particular instance of a class and would like to return different output of it’s method based on the number of time this method is called.

In the below example, we are writing a test for validItemsForUser method of OrderService. Using the doAnswer, the userSerivceSpy will return true when isItemAllowed method is called 3 or 5 times, else it will return false.

@Test
public void testValidItemsForAUser_spyWithDoAnswer() {
  List<String> items = Arrays.asList(“Item1”, “Item2”, “Item3”, “Item4”, “Item5”);
  mockOrder = mock(Order.class);
  Mockito.when(mockOrder.getItems()).thenReturn(items);

  OrderService orderService = new OrderService(mockOrder, mockUser);

  UserService userService = orderService.getUserService();
  userServiceSpy = spy(userService);
  orderService.setUserService(userServiceSpy);

  final int[] callCount = {0};
  //Use this when there is a while loop, and you want to return different output of a method based on the number of time the loop has run
  Mockito.doAnswer((Answer<Boolean>) invocation -> {
      callCount[0]++;
      return callCount[0] == 5 || callCount[0] == 3;
  }).when(userServiceSpy).isItemAllowed();

  int actualResult = orderService.validItemsForAUser();

  verify(userServiceSpy, times(5)).isItemAllowed();
  Assertions.assertEquals(2, actualResult);
}

Testing using Reflection

Pattern: When you want to change the value of a private field to simulate certain test scenarios.

In the below example, we are writing a test for createOrder method of OrderService.

Here, the OrderService has a private field orderStale. We are using reflection API Field to get access to this field and setting a different value (in this example “Stale”) to simulate a test scenario.

@Test
public void testCreateOrder_modifyPrivateFieldUsingReflectionAPI() throws NoSuchFieldException, IllegalAccessException {
  mockOrder = mock(Order.class);
  Mockito.when(mockOrder.orderSize()).thenReturn(10);

  OrderService orderService = new OrderService(mockOrder, mockUser);
  UserService userService = orderService.getUserService();
  userServiceSpy = spy(userService);
  orderService.setUserService(userServiceSpy);

  Field logReader = orderService.getClass().getDeclaredField(“orderStale”);
  logReader.setAccessible(true);
  logReader.set(orderService, “Stale”);

  int actualResult = orderService.createOrder();

  Assertions.assertEquals(OrderService.ORDER_STALE, actualResult);
}

Categories