Mockito

Mockito Captor Example

A unit test should test a class in isolation. Side effects from other classes or the system should be eliminated if possible. Mockito lets you write beautiful tests with a clean & simple API. In this example we will learn how to use ArgumentCaptor class/ Captor annotation of Mockito. Tools and technologies used in this example are Java 1.8, Eclipse Luna 4.4.2

1. Introduction

Mockito is a popular mocking framework which can be used in conjunction with JUnit. Mockito allows us to create and configure mock objects. Using Mockito simplifies the development of tests for classes with external dependencies significantly. We can create the mock objects manually or can use the mocking framewors like Mockito, EasyMock. jMock etc. Mock frameworks allow us to create mock objects at runtime and define their behavior. The classical example for a mock object is a data provider. In production a real database is used, but for testing a mock object simulates the database and ensures that the test conditions are always the same.

2. Creating a project

Below are the steps we need to take to create the project.

  1. Open Eclipse. Go to File=>New=>Java Project. In the ‘Project name’ enter ‘MockitoCaptorExample’.
  2. Eclipse will create a ‘src’ folder. Right click on the ‘src’ folder and choose New=>Package. In the ‘Name’ text-box enter ‘com.javacodegeeks’. Click ‘Finish’.
  3. Right click on the package and choose New=>Class. Give the class name as MockitoCaptorExample. Click ‘Finish’. Eclipse will create a default class with the given name.

2.1 Dependencies

For this example we need the junit and mockito jars. These jars can be downloaded from Maven repository. We are using ‘junit-4.12.jar’ and ‘mockito-all-1.10.19.jar’. There are the latests (non-beta) versions available as per now. To add these jars in the classpath right click on the project and choose Build Path=>Configure Build Path. The click on the ‘Add External JARs’ button on the right hand side. Then go to the location where you have downloaded these jars. Then click ok.

3. ArgumentCaptor class

ArgumentCaptor class is used to capture argument values for further assertions. Mockito verifies argument values in natural java style: by using an equals() method. This is also the recommended way of matching arguments because it makes tests clean & simple. In some situations though, it is helpful to assert on certain arguments after the actual verification. For example:

ArgumentCaptor<Contact> argument = ArgumentCaptor.forClass(Contact.class);
verify(mockClass).doSomething(argument.capture());
assertEquals("Meraj", argument.getValue().getName());

It is recommended to use ArgumentCaptor with verification but not with stubbing. Using ArgumentCaptor with stubbing may decrease test readability because captor is created outside of assert (aka verify or ‘then’) block. Also it may reduce defect localization because if stubbed method was not called then no argument is captured.

In a way ArgumentCaptor is related to custom argument matchers. Both techniques can be used for making sure certain arguments where passed to mocks. However, ArgumentCaptor may be a better fit if:

  • custom argument matcher is not likely to be reused
  • you just need it to assert on argument values to complete verification

Custom argument matchers via ArgumentMatcher are usually better for stubbing.

3.1 Methods

In this section we will describe the methods defined in the ArgumentCaptor class.

3.1.1 public T capture()

Use it to capture the argument. This method must be used inside of verification. Internally, this method registers a special implementation of an ArgumentMatcher. This argument matcher stores the argument value so that you can use it later to perform assertions.

3.1.2 public T getValue()

Returns the captured value of the argument. If the method was called multiple times then it returns the latest captured value.

3.1.3 public java.util.List<T> getAllValues()

Returns all captured values. Use it in case the verified method was called multiple times.

4. Captor annotation

Captor annotation allows shorthand ArgumentCaptor creation on fields. One of the advantages of using @Captor annotation is that you can avoid warnings related capturing complex generic types. The Captor annotation is defined as below:

@Retention(value=RUNTIME)
@Target(value=FIELD)
@Documented
public @interface Captor

5. Code

In this section first we wee see a simple example of using the @Captor annotation. Then we will discuss a more complex one.

5.1 Simple Code

For this simple example we will make use of the java.util.Stack class. We will create a stack of strings then add one value to it. Then we will capture he argument and verify it. Below is the code snippet for this:

stack.add("Java Code Geeks");
Mockito.verify(stack).add(argumentCaptor.capture());
assertEquals("Java Code Geeks", argumentCaptor.getValue());

In the second example we will add two values in the Stack and will extract all the values using the getAllValues() method. Below is the code snippet for this:

stack.add("Java Code Geeks");
stack.add("Mockito");
Mockito.verify(stack, Mockito.times(2)).add(argumentCaptor.capture());
List<String> values = argumentCaptor.getAllValues();
assertEquals("Java Code Geeks", values.get(0));
assertEquals("Mockito", values.get(1));

Below is the code which shows the usage of @Captor annotation

MockitoCaptorExample.java

package com.javacodegeeks;

import static org.junit.Assert.assertEquals;

import java.util.Stack;

import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

public class MockitoCaptorExample {

  @Mock Stack<String> stack;
  @Captor ArgumentCaptor<String> argumentCaptor;

  @Before
  public void setUp() {
    MockitoAnnotations.initMocks(this);
  }

  @Test
  public void test() throws Exception {
    stack.add("Java Code Geeks");
    Mockito.verify(stack).add(argumentCaptor.capture());
    assertEquals("Java Code Geeks", argumentCaptor.getValue());
  }
}

5.2 Stub example

In this section we will see how we can use @Captor for stubbing. We will use the report generation example.

Create an interface with one method.

IReportGenerator.java

package com.javacodegeeks;

/**
* Interface for generating reports.
* @author Meraj
*/
public interface IReportGenerator {

  /**
  * Generate report.
  * @param report Report entity.
  */
  void generateReport(ReportEntity report);
}

Now we will create the report entity class which is a simple POJO class.

ReportEntity.java

package com.javacodegeeks;

import java.util.Date;

/**
* Report entity.
* @author Meraj
*/
public class ReportEntity {

  private Long reportId;
  private Date startDate;
  private Date endDate;
  private byte[] content;

  public Long getReportId() {
    return reportId;
  }

  public void setReportId(Long reportId) {
    this.reportId = reportId;
  }

  public Date getStartDate() {
    return startDate;
  }

  public void setStartDate(Date startDate) {
    this.startDate = startDate;
  }

  public Date getEndDate() {
    return endDate;
  }

  public void setEndDate(Date endDate) {
    this.endDate = endDate;
  }

  public byte[] getContent() {
    return content;
  }

  public void setContent(byte[] content) {
    this.content = content;
  }
}

Now we will have a look at the service class which we will use to generate the report.

ReportGeneratorService.java

package com.javacodegeeks;

import java.util.Date;

/**
* Service class for generating report.
* @author Meraj
*/
public class ReportGeneratorService {

  private IReportGenerator reportGenerator;

  /**
  * Generate report.
  * @param startDate start date
  * @param endDate end date
  * @param content report content
  */
  public void generateReport(Date startDate, Date endDate, byte[] content) {
    ReportEntity report = new ReportEntity();
    report.setContent(content);
    report.setStartDate(startDate);
    report.setEndDate(endDate);
    reportGenerator.generateReport(report);
  }
}

Now we will look at the test.

ReportGeneratorServiceTest.java

package com.javacodegeeks;

import static org.junit.Assert.assertEquals;

import java.util.Calendar;

import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

public class ReportGeneratorServiceTest {

  @InjectMocks private ReportGeneratorService reportGeneratorService;
  @Mock private IReportGenerator reportGenerator;
  @Captor private ArgumentCaptor<ReportEntity> reportCaptor;

  @Before
  public void setUp() {
    MockitoAnnotations.initMocks(this);
  }

  @SuppressWarnings("deprecation")
  @Test
  public void test() {
    Calendar startDate = Calendar.getInstance();
    startDate.set(2016, 11, 25);
    Calendar endDate = Calendar.getInstance();
    endDate.set(9999, 12, 31);
    String reportContent = "Report Content";
    reportGeneratorService.generateReport(startDate.getTime(), endDate.getTime(), reportContent.getBytes());

    Mockito.verify(reportGenerator).generateReport(reportCaptor.capture());

    ReportEntity report = reportCaptor.getValue();

    assertEquals(116, report.getStartDate().getYear());
    assertEquals(11, report.getStartDate().getMonth());
    assertEquals(25, report.getStartDate().getDate());

    assertEquals(8100, report.getEndDate().getYear());
    assertEquals(0, report.getEndDate().getMonth());
    assertEquals(31, report.getEndDate().getDate());

    assertEquals("Report Content", new String(report.getContent()));
  }
}

6. Download the source file

This was an example of Mockito Captor annotation.

Download
You can download the full source code of this example here: Mockito Captor Example

Mohammad Meraj Zia

Senior Java Developer
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button