Unit Testing With Mockito
Learn how to write effective unit tests with the Mockito framework as part of Test Driven Development.
Join the DZone community and get the full member experience.
Join For FreeWe take great effort to build a system or a platform with thousands of lines of code and test cycles to ensure the quality and coverage. With QA validation being already part of the cycle, unit testing should also be considered a critical pre-step before turning the code into the build and deployment. Thus, making sure the build is successful locally and achieving expected code coverage.
TDD (Test Driven Development) is an effective way of developing the system by incrementally adding the code and writing tests. Unit tests can be written using the Mockito framework.
In this article, we explore a few areas of writing unit tests using the Mockito framework. We use Java for the code snippets here.
What Does Mocking Mean?
Mocking is a way of producing dummy objects, operations, and results as if they were real scenarios. This means that it deals with no real database connections and no real server up and running. However, it mimics them so that the lines are also covered and expect the actual result. Thus, it can be compared with the expected result and asserted.
Mockito is a framework that facilitates mocking in the tests. Mockito objects are kind of proxy objects that work on operations, servers, and database connections. We would be using dummy, fake, and stub wherever applicable for mocking.
We will use JUnit with the Mockito framework for our unit tests.
Stepping Into Mocking
We have a composition in a Java class: StockPlan.java
public class Account {
public List < String > getPlans(String accountId) { ...
}
}
public class StockPlan {
private Account account;
public StockPlan(Account account) {
this.account = account;
}
public List < String > getPlans(String accountId)
return account.getPlans(accountId);
}
//....
}
Now, we will see the test for the StockPlan class:
...
import org.junit.Before;
import org.junit.Test;
import org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.any;
public class StockPlanTest {
Account account;
StockPlan stockPlan;
@Before
public void setUp() {
account = mock(Account.class);
stockPlan = new StockPlan(account):
}
@Test
public void verify_account_plans_returned_successfully() {
List < String > planNames = Arrays.asList("plan123"); //A
when(account.getPlans(any(String.class)).thenReturn(planNames); //B
List < String > resultPlans = stockPlan.getPlans("account123"); //C
assertEquals(resultPlans, planNames); //D
}
}
More Insights Into the Code
In line A, we are creating an expected list of plan names that the Account object would return.
In line B, we mock (fake) the invocation of getPlans in Account. Thus, when it tries to invoke the method, it returns the expected list of plan names.
In line C, we make the actual call to the StockPlan object to get the plan names. It will invoke getPlans in the Account class and return the expected list of plan names (line A).
In line D, we compare the list of plans and verify that they are the same.
Mocking Void Methods With Spy
In the same class level, if a method calls another which is void, we mock the call by creating a "spy" on the original object. For instance:
public class StockPlan {
Account account;
public StockPlan(Account account) {
this.account = account;
}
public Boolean updateAccount(Account accountRecord) {
validate(accountRecord);
....
//save to db. Once successful, return true.
return true;
}
public void validate(Account account) {
//validate account fields...
}
//....
}
The test would look like this:
@Test
public void verify_account_updated successfully() {
Account account = mock(Account.class);
StockPlan stockPlan = new StockPlan(account);
StockPlan spyPlan = spy(stockPlan);
doNothing().when(spyPlan).validate(account);
Boolean result = spyPlan.updateAccount(account);
assertTrue(result);
}
Mocking a Thrown Exception and Expecting It in the Test
Suppose a real method throws an exception and we need to test it. We can do this by faking as if the exception is thrown and expect any custom exceptions in the test. For instance, we have our custom exception WriteDBException class like so:
public WriteDBException extends RunTimeException {
public WriteDBException(String msg) {
super(msg);
}
}
Our StockPlan class is modified with a lombok annotation and with exception handling:
@Slf4j
public class StockPlan {
Account account;
DBRepository repository;
public StockPlan(Account account, DBRepository repository) {
this.account = account;
this.repository = repository;
}
public Boolean updateAccount(Account account) throws WriteDBException {
try {
repository.update(account, "account_table_name");
} catch (Exception e) {
log.error("Failure in updating Account record with message={}", e.getMessage());
throw new WriteDBException(e.getMessage());
}
//.....
}
}
Then the test would look like this:
@Test(expected = WriteDBException.class)
public void verify_exception_thrown_when_updating_account_record_in_db() {
Account account = mock(Account.class);
DBRepository repository = mock(DBRepository.class);
StockPlan stockPlan = new StockPlan(account, repository);
when(repository.update(account, any(String.class))).thenThrow(new RunTimeException("FAILURE));
//this would throw WriteDBException and log message would also be printed out
stockPlan.updateAccount(account);
}
Mocking REST Controller APIs
We would see how we can mock REST calls, for instance - read method.
Say there is a read method in the controller (created with Spring MVC with RequestMapping),
@RestController
@RequestMapping("/stockplans")
public class StockPlanController {
.......
@RequestMapping(value = "/stocks/{stockId}/", method = RequestMethod.GET) public ResponseEntity < List < StockPlan >> read(
@PathVariable String stockId,
@RequestParam(value = "planId", ) String planId) {
.....
//read list from service --> database
ResponseEntity < List < StockPlan >> stockPlanList = createStockPlanList(......);
return stockPlanList;
}
This read method returns a list of StockPlan objects in JSON format. Now, we write our test.
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
public class StockPlanControllerTest {
@Autowired
public WebApplicationContext context;
public MockMvc mockMvc;
@Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
@Test
public void verify_stock_plans_read_successfully() {
mockMvc.perform(get("/stockplans/stocks/ABC/")) //line A
.param("planId", "plan123") //line B
.andExpect(status().isOk()) //line C
.andExpect(content().contentType("application/json;-8)) //line D
}
}
In line A, we mock the read API call by specifying the endpoint with a value for param (dynamically). Thus, value ABC is for stockId in the path parameter. It is mandatory, otherwise, the endpoint is invalid.
In line B, we provide the value for RequestParam - planId.
In line C, we expect the status returned as OK by posting those values at the URL (endpoint + values). If the invocation is successful, it will return "OK." Otherwise, it will return any of the HTTP error codes.
Line D tells us that the expected content should be JSON.
This is the shortest version of testing REST read calls. Note that we have used a few new classes for our test. The following classes need to be imported into the test.
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
In a similar way, we can test the create operation as well in the controller.
public class StockPlanController {
....
@RequestMapping(value = "/", method = RequestMethod.POST)
public ResponseEntity < StockPlanResponse > create(
@RequestBody @Valid StockPlan stockPlan, HttpServletRequest request,
HttpServletResponse response) {
//validate and send to service for saving the StockPlan into database
//create StockPlanResponse and return....
}
The test will look like this:
mockMvc.perform(post("/stockplans/")
.contentType(MediaType.APPLICATION_JSON)
.content(stockPlan_object_converted_to_string)
.andExpect(status().isOk());
Testing Controller Exceptions With MvcResult
Suppose that the read API in the controller throws an exception (for instance, NoDataFoundException). Then, we can capture the result actions and verify that the thrown exception is NoDataFoundException:
ResultActions actions = mockMvc.perform(get(....)).andExpect(status().isNotFound())....
MvcResult result = actions.andReturn();
assertThat(result.getResolvedException().getClass(), typeCompatibleWith(NoDataFoundException.class));
It is important to note that the exception NoDataFoundException is a custom exception and it has to be thrown from your global exception handler, which is of the type ControllerAdvice.
Developing unit tests is the best way of developer validation.
Opinions expressed by DZone contributors are their own.
Comments