Mocking a void method in the same class that is under test with mockito and junit5?

i want to mock a void method in the same class that is under test with mockito. i can do that for not void method with annotating test class with @Spy and then use below code to return data i want.

        willAnswer(arg -> arg.getArgument(0))
            .given(customerService)
            .saveCustomer(any(Customer.class));

but how can do that for void method. (i already try willDoNothing().given() function)

this is my real method:

public void deleteCustomersByTenantId(TenantId tenantId) throws ThingsboardException {
    log.trace("Executing deleteCustomersByTenantId, tenantId [{}]", tenantId);
    Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
    customersByTenantRemover.removeEntities(tenantId, tenantId);
}

public void deleteCustomer(TenantId tenantId, CustomerId customerId) throws ThingsboardException {
    log.trace("Executing deleteCustomer [{}]", customerId);
    Validator.validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
    Customer customer = findCustomerById(tenantId, customerId);
    if (customer == null) {
        throw new IncorrectParameterException("Unable to delete non-existent customer.");
    }
    entityViewService.unassignCustomerEntityViews(customer.getTenantId(), customerId);
    assetService.unassignCustomerAssets(customer.getTenantId(), customerId);
    userService.deleteCustomerUsers(customer.getTenantId(), customerId);
    deleteEntityRelations(tenantId, customerId);
    entityGroupDao.deleteEntityGroupsByTenantIdAndCustomerId(customer.getTenantId(), customerId);
    customerDao.removeById(tenantId, customerId.getId());
}

private final PaginatedRemover<TenantId, Customer> customersByTenantRemover =
        new PaginatedRemover<>() {

            @Override
            protected PageData<Customer> findEntities(TenantId tenantId, TenantId id, PageLink pageLink) {
                return customerDao.findCustomersByTenantId(id.getId(), pageLink);
            }

            @Override
            protected void removeEntity(TenantId tenantId, Customer entity) throws ThingsboardException {
                deleteCustomer(tenantId, new CustomerId(entity.getUuidId()));
            }
        };

and this is the test:

void deleteCustomersByTenantId() throws ThingsboardException {
    //given
    Customer customer = new Customer();
    customer.setId(CUSTOMER_ID);
    customer.setTenantId(TENANT_ID);

    ArgumentCaptor<CustomerId> customerIdArgumentCaptor = ArgumentCaptor.forClass(CustomerId.class);

    var pageData = new PageData<>(Collections.singletonList(customer), 1 ,1 , false);

    given(customerDao.findCustomersByTenantId(any(UUID.class), any(PageLink.class)))
            .willReturn(pageData);
    doNothing()
            .when(customerService)
            .deleteCustomer(any(), any());

    //when
    customerService.deleteCustomersByTenantId(TENANT_ID);

    //then
    then(customerDao)
            .should(times(1))
            .findCustomersByTenantId(any(UUID.class), any(PageLink.class));
    then(customerService)
            .should(times(1))
            .deleteCustomer(any(TenantId.class), customerIdArgumentCaptor.capture());
    assertEquals(CUSTOMER_ID, customerIdArgumentCaptor.getValue());
}

this is the Mocks:

//other mocks...

@Mock
private RelationService relationService;

@Mock
private CustomerValidator customerValidator;

@InjectMocks
@Spy
private CustomerServiceImpl customerService;

every dependency is mock with @Mock.

Answer

Do nothing works for me. Just pay attention to the calling order

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;

doNothing().when(yourMock).yourMethod(yourArgs);

This thread treats a similar problem, using a spy to mock specific methods with static factories should work. I’ve seen people doing to reverse operation (Use a Mock and then call the real method in some specific cases), but never tried it personally..