0

How to verify that my sendMessage method is called with Msg.SAVE or with Msg.UPDATE using mockito's verify method.

@Service
public class CustomerService {
private final CustomerRepository customerRepository;
private final SendMessageInTopic messageInTopic;
public CustomerService (CustomerRepository customerRepository,  SendMessageInTopic messageInTopic) 
 {
    this.customerRepository = customerRepository;
    this.messageInTopic= messageInTopic;
 }

public Customer saveCustomer(Customer savedCustomer) {
        Customer customer =    customerRepository
                .findCustomerByEmail(savedCustomer.getEmail());

        if(customer != null) {
            customer.setFirstName(savedCustomer.getFirstName());
            customer.setLastName(savedCustomer.getLastName());
            customer.setPhoneNumber(savedCustomer.getPhoneNumber());
            messageInTopic.sendMessage(savedCustomer.getId(),Msg.SAVE)
        } else {
            customer = new Customer();
            customer.setFirstName(savedCustomer.getFirstName());
            customer.setLastName(savedCustomer.getLastName());
            customer.setEmail(savedCustomer.getEmail());
            customer.setPhoneNumber(savedCustomer.getPhoneNumber());
            messageInTopic.sendMessage(savedCustomer.getId(),Msg.Update)
        }
        return customerRepository.save(customer);
    }
}

public class CustomerServiceTest{
private final CustomerRepository customerRepository = mock(CustomerRepository.class);
private final CustomerService customerService = mock(CustomerService.class);
private final SendMessageInTopic  messageInTopic = mock(SendMessageInTopic.class);
private final CustomerMapper customerMapper = mock(CustomerMapper.class);
    @Test
    void whenSaveCustomerThenMsgSAVEOrUPDATE() {
    // Given

    final var customerId = "123456";
    final var customer =
        new CustomerDto(ensUserId, 'james', 'GREWAN', '32766666', 'j.grewan@gree.com');

    final customerEntity = mock(Customer.class);

    when(customerRepository.findCustomerByEmail(customer.getEmail())
        .thenReturn(Optional.of(customerEntity));
    when(customerRepository.save(customerEntity)).thenReturn(customerEntity);
    when(customerMapper.toDto(customerEntity)).thenReturn(customer);

    // When

    final var result = customrService.saveCustomer(customer);

    // Then

    assertEquals(result, customer);
    verify(messageInTopic, times(1)).sendMessage(result.getId(),Msg.SAVE);
  }
}

verify(messageInTopic, times(1)).sendMessage(result.getId(),Msg.SAVE); : This is where I will test if I call my sendMessage method with the Msg.SAVE parameter in the create case or the Msg.UPDATE parameter in the update case.

how to do this test please ?

obela06
  • 307
  • 5
  • 15
  • You can use `Matchers.eq` to match a specific parameter value – Jorn Aug 15 '23 at 09:07
  • Hi @Jorn, can you show me an example to explain me how can i us Matchers.eq with my two parameters SAVE or UPDATE – obela06 Aug 15 '23 at 09:21
  • There are a ton of examples for that available on Google – Jorn Aug 15 '23 at 09:23
  • @Jorn, in can do this example : verify( messageInTopic, times(1))sendMessage(result.getId(),eq( Msg.SAVE),eq( Msg.UPDATE)); my sendMessage method must have two arguments , the id and the string Msg.SAVE or Msg.UPDATE. – obela06 Aug 15 '23 at 09:27
  • 1
    You are mocking _everything_, even your service! What are you actually testing? Correct, _nothing_ except your mock setup. Your real `CustomerService.saveCustomer` logic is never executed (and thus `sendMessage` never called) – knittl Aug 15 '23 at 11:50

2 Answers2

0

@knittl has it correct. Your verify call is correct. The problem is you're mocking the class you're trying to test. When you call mock(CustomerService.class), Mockito is actually creating a class that stubs all the methods (i.e. overrides them and returns a default):

class CustomerServiceMock extends CustomerService {
  ...
  @Override
  public Customer saveCustomer(Customer savedCustomer) {
    // Stub
    return null;
  }
  ...
}

So customerService.saveCustomer(customer) will return null since customerService is actually a CustomerServiceMock. assertEquals(result, customer) will fail because of this. The solution is to replace customerService = mock(CustomerService.class); with customerService = new CustomerService(customerRepository, messageInTopic). Note that customerRepository and messageInTopic are the mocks you've declared.

HOWEVER, your assertEqual should still fail because of these lines.

when(customerRepository.findCustomerByEmail(customer.getEmail())
        .thenReturn(Optional.of(customerEntity));
when(customerRepository.save(customerEntity)).thenReturn(customerEntity);

These make it so that saveCustomer(customer) returns the mock customerEntity.

  1. the assertEquals fails because customerEntity does not match customer.
  2. the verify(messageInTopic ... also won't work because sendMessage(result.getId(),Msg.SAVE) is equivalent to sendMessage(null, Msg.SAVE). This is because customerEntity is a mock(Customer.class), which means that customerEntity.getId looks like this:
class CustomerMock extends Customer {
...

  public String getId() {
    // Stub
    return null;
  }
...
}

There are many ways to fix this. My recommendation is to just remove customerEntity and use customer like so:

when(customerRepository.findCustomerByEmail(customer.getEmail())
        .thenReturn(Optional.of(customer));
when(customerRepository.save(customer)).thenReturn(customer);

This will make it so that saveCustomer returns customer so that the assertEqual passes. It will also make it so that savedCustomer.getId() and result.getId() are equal. findCustomerByEmail is supposed to return the real customer anyways, so this makes the most sense to me.


Also just as an FYI: Matchers.eq and just directly using the argument are functionally the same, so it won't change anything:

verify(messageInTopic, times(1)).sendMessage(result.getId(),Msg.SAVE);
verify(messageInTopic, times(1)).sendMessage(Matchers.eq(result.getId()), Matchers.eq(Msg.SAVE));

So why use Matchers.eq at all? Well let's say you only want to verify that sendMessage is called with Msg.SAVE vs. Msg.UPDATE and you don't care about the id. The problem is that you CANNOT mix matchers and direct arguments, so this doesn't work:

verify(messageInTopic, times(1)).sendMessage(Matchers.any(), Msg.SAVE);

But this will work:

verify(messageInTopic, times(1)).sendMessage(Matchers.any(), Matchers.eq(Msg.SAVE));

Also as a second FYI, there are many different ways to verify arguments. Here's a post with information about all the different ways: Mockito. Verify method arguments. This is especially important if you want to verify arguments that are objects.

Keji C
  • 21
  • 4
0

There are several ways to do it. For example, using captors:

First declare a captor of the argument you want to check in the test class:

@Captor
private ArgumentCaptor<Msg.class> msgCaptor;

Then in the test, verify and assert the correct value:

verify(messageInTopic).sendMessage(result.getId(), msgCaptor.capture());
// If using org.assertj.core.api.Assertions.assertThat;
assertThat(msgCaptor.getValue()).isEqualTo(Msg.SAVE);
Eduardo Yáñez Parareda
  • 9,126
  • 4
  • 37
  • 50