Search code examples
javaunit-testingjunitmockitoargumentcaptor

Proper usage of mockito ArgumentCaptor in Java?


I have looked at several usage examples of e.g. Using Mockito ArgumentCaptor, but I have confused about the proper usage for the following scenario:

Here is the method that I want to test:

@Override
public ProductDTO create(Product product, UUID uuid) {
        
    // code omitted for brevity 
        
    final Product saved = productRepository.save(product);
    final Currency currency = currencyService.getCurrencyByUuid(uuid);
        
    return new ProductDTO(saved, currency);
}

My test method:

@RunWith(MockitoJUnitRunner.class)
public class ProductServiceImplTest {

    @Mock
    private ProductRepository productRepository;

    @Mock
    private CurrencyService currencyService;

    @InjectMocks
    private ProductServiceImpl productService;

    @Captor
    private ArgumentCaptor<Product> productCaptor;

    @Test
    public void test_create() {
        UUID uuid = UUID.fromString("00000000-0000-0000-0000-000000000001");
        UUID productUuid = UUID.fromString("00000000-0000-0000-0000-000000000222");
        Currency currency = Currency.getInstance("EUR");        
        Product product = new Product();
        productCostBySite.setProductUuid(productUuid);

        // 1. prepare the values
        when(productRepository.save(productCaptor.capture())).thenReturn(product);
        when(currencyService.getCurrencyByUuid(uuid)).thenReturn(currency);
                
        // 2. call the service method that is tested
        ProductDTO result = productService.create(product);

        // 3. Check if the captured value is the expected one
        Product value = productCaptor.getValue();           
        assertEquals(productUuid, value.getProductUuid());
        assertEquals(currency.getCurrencyCode(), 
            result.getCurrency().getCurrencyCode());
    }
}

Here are the questions (the numbers are also the comment numbers of test method):

1. I tested productRepository.save method using ArgumentCaptor and currencyService.getCurrencyByUuid(uuid) by returned value as it is not a repository call. Is that true?

2. I called the service method, but in some examples verify method is used. I am not sure if I need to use verify method for this example? Any idea?

3. I used the returned value for service and used captured value for repository. Is that a correct approach?


Solution

  • Eugene is right, you don't need an ArgumentCaptor. To answer the question of when you need it: if you want to verify the value of a transatory object you don't have access to before or after your method.

    For example, in

    boolean createAndSave(String productName) {
      Product p = new Product(productName);
      return repository.save(p);
    }
    

    you don't have access to the created Product but want to verify it was created correctly. To do this, you can capture it:

    ArgumentCaptor<Product> savedCaptor = ArgumentCaptor.forClass(Product.class);
    service.createAndSave("name");
    verify(repository).save(savedCaptor.capture()); // get the product being saved
    Product saved = savedCaptor.getValue();
    assertEqual("name", saved.getName());
    

    Now, you make sure that the repository was used to save a product with the name you gave the service.

    In your case, you can just use when(repository.save(any()).thenReturn(product) with a product you created ahead of time.