简体   繁体   中英

Mockito ArgumentCaptor needs stubbing even I use verify

I am trying to write a Unit Test for the following service method:

public CommandDTO update(UUID uuid, QuantityRequest request) {
        Quantity quantity = quantityRepository.findByUuid(uuid)
                        .orElseThrow(() -> new EntityNotFoundException(QUANTITY_ENTITY_NAME));
        Quantity updated = saveQuantity(quantity, request);
        return CommandDTO.builder().uuid(updated.getUuid()).build();
}

private Quantity saveQuantity(Quantity quantity, QuantityRequest request) {
        //mapp fields (code omitted for brevity)
        return quantityRepository.save(quantity);
}

I use ArgumentCaptor so that I catch the quantity parameter in the private method that my service method calls: quantityRepository.save(quantity) .

@Test
public void test() {
    Quantity quantity = new Quantity();

    QuantityRequest request = new QuantityRequest();
    request.setQuantity(100);

    when(quantityRepository.findByUuid(uuid)).thenReturn(Optional.of(quantity));

    // It seems to be meaningless this stubbing. because I already stb it in verify method below
    when(quantityRepository.save(any())).thenReturn(quantity);

    quantityService.update(uuid, request);

    verify(quantityRepository).save(quantityCaptor.capture());
    Quantity captured = quantityCaptor.getValue();

    // assertions...
}

The test is working, but if I remove when(quantityRepository.save(any())).thenReturn(quantity); line, it throws "null pointer exception error" because in this case updated parameter in the update method is null. So, do I have to use the mentioned stubbing in the when() method? I think I do not need and the verify already perform that task via verify(quantityRepository).save(quantityCaptor.capture()) . Is that true?

The problem lies in the following lines:

  Quantity updated = saveQuantity(quantity, request);
  return CommandDTO.builder().uuid(updated.getUuid()).build();

Which is essentially the same as:

 Quantity updated = quantityRepository.save(quantity)
 return CommandDTO.builder().uuid(updated.getUuid()).build();

The stubbing is necessary, because you're expecting the save method to return something, when you call updated.getUuid() . Without the stub, updated is null and your call results in a NullPointerException .

No, you need the stubbing here. You can't delete the when(save) call because your test depends on what return value you have for save . However, you're on the right track to question whether any given thing needs to be both stubbed and verified.


You're right that it's often redundant to verify things that you've stubbed, and the docs to verify tell you so :

Although it is possible to verify a stubbed invocation, usually it's just redundant . Let's say you've stubbed foo.bar() . If your code cares what foo.bar() returns then something else breaks(often before even verify() gets executed). If your code doesn't care what foo.bar() returns then it should not be stubbed.

And as attributed to Aaron Jensen, quoted in " Asking and Telling " by Mockito's original author Szczepan Faber:

If you're verifying you don't need to stub unless of course that method returns something that is critical to the flow of your test (or code), in which case you don't really need to verify, because the flow would have verified.

In general, if you've stubbed something, then you make your test assertions at the end and you don't need to test that the method was called—the assertions would fail. And if you verify that a method was called but nobody cares about the result—or the result is passed directly back to the caller—then you might not need to stub, because Mockito's default return values like 0 and null should work fine.

Here, neither of those are true: A default null value causes an NPE, and you need to verify because that's the only supported way to get a value out of your ArgumentCaptor. This means that you call verify , but not for the sake of actual verification, but rather to get the value out of ArgumentCaptor. Part of the verify call is redundant, but there isn't another practical way to get to the necessary ArgumentCaptor part, so your code is fine with both when and verify .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM