Capture parameters with Mockito ArgumentCaptor

While creating test methods for a class, you might want to access some arguments passed to a specific method. Luckily, you can capture parameters with Mockito ArgumentCaptor. It is especially useful when you can’t access the argument from the outside of the method.

Let’s check it out on a basic example. Let’s say we have a class called Product and we create stock codes for these objects before saving them to the database. So we’ve created a method called generateProductCodeAndSave() which generates the code then passes the product to another method in order to insert it to the database.

public class Product {
    private String stockCode;
    private String brand;
    private String model;

    //getter setter constructor
}
@Component
public class ProductOperations {

    ProductDB productDb = new ProductDB();

    void generateProductCodeAndSave(Product product) {
        product.setStockCode(product.getBrand() + "_" + product.getModel());
        productDb.saveProductToDB(product);
    }
}

So what we want to create a test that will check if our generateProductCodeAndSave() setting the stock codes of our products as expected. The only way we can do this is to use ArgumentCaptor. In this way, we will be able to see what our saveProductToDB() method is getting as a parameter. So we need to set an ArgumentCaptor on this method.

@Test
void shouldSuccessfullyGenerateStockCode() {
    Product product = new Product("GORDONS", "LONDON");

    productOperations.generateProductCodeAndSave(product);

    ArgumentCaptor<Product> argument = ArgumentCaptor.forClass(Product.class);
    Mockito.verify(productDbMock).saveProductToDB(argument.capture());

    assertEquals("", argument.getValue().getStockCode(), "GORDONS_LONDON");
}

After calling the method, generateProductCodeAndSave() we’ve created an instance of ArgumentCaptor by giving the class type of the expected parameter. In this case, it’s Product class. You must take note that productDbMock is a mock instance, a normal instance will cause it to throw NotAMockException.

Now we are ready to use it in combined with verify. We will call our method that we want to capture parameters from. Then we will call our method after verify and place our ArgumentCaptors as parameters. After this point, we will be able to access the captured parameters from argument. Calling getValue() will return the Product object. Then we can call assertEquals on this instance.

Warning

There are two things I want to note which are also noted on Mockito documentation.

  • It is not recommended to use stubbing with ArgumentCaptor because it may decrease readability and also reduce defect localization. Calling ArgumentCaptor with verify is the best option.
  • This class doesn’t do any type checks and generic signatures are only used to avoid casting.

Leave a Reply