10

I have the following test class:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
public class TransactionServiceTests {

    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Mock
    private MessagingService mockMessagingService;
    @Mock
    private CustomerRepository mockCustomerRepository;

    @Autowired
    TransactionService transactionService;

    @Test
    public void testTransactionBetweenCustomersAndBalanceOfReceiver() {

        int AMOUNT = 50;

        // prepare your test data unless you always expect those values to exist.
        Customer customerReceiver = new Customer();
        customerReceiver.setName("TestReceiver");
        customerReceiver.setBalance(12);
        mockCustomerRepository.save(customerReceiver);

        Customer customerSender = new Customer();
        customerSender.setName("TestSender");
        customerSender.setBalance(50);
        mockCustomerRepository.save(customerSender);

        int expectedReceiverAmount = customerReceiver.getBalance() + AMOUNT;
        int expectedSenderAmount = customerSender.getBalance() - AMOUNT;
        transactionService.makeTransactionFromSenderToReceiver(customerSender, customerReceiver, AMOUNT);

        assertEquals(expectedSenderAmount, customerSender.getBalance());
        assertEquals(expectedReceiverAmount, customerReceiver.getBalance());

    }
}

This is the TransactionService. class itself:

@Service
public class TransactionService {

    private MessagingService messagingService;
    private CustomerRepository customerRepository;

    private static final Logger log = LoggerFactory.getLogger(TransactionService.class);

    @Autowired
    public TransactionService(MessagingService messagingService, CustomerRepository customerRepository){
        Assert.notNull(messagingService, "MessagingService cannot be null");
        this.messagingService = messagingService;
        Assert.notNull(customerRepository, "CustomerRepository cannot be null");
        this.customerRepository = customerRepository;
    }

    public void makeTransactionFromSenderToReceiver(Customer sender, Customer receiver, int amount) {

        if (sender.getBalance() >= amount) {
            sender.setBalance(sender.getBalance() - amount);
            receiver.setBalance(receiver.getBalance() + amount);

            customerRepository.save(sender);
            customerRepository.save(receiver);
        }

        else {    
            throw new RuntimeException();
        }
    }
}

During the test, it is adding the above mentioned users to my live database and leaving them there even after the tests are finished. Can I in some way tell Mockito to not touch my database? Or is that totally not possible?

Deniss M.
  • 3,617
  • 17
  • 52
  • 100
  • 2
    Just because you define a mock instance in a test doesn't mean all objects will start using that mock instance. You need to inject the mock repository into the service under test. Forget about SpringJUnit4ClassRunner for unit test. Just use `new TransactionService(mockMessagingService, mockCustomerRepository)`. – JB Nizet Sep 11 '16 at 13:33
  • Also, calling `mockCustomerRepository.save(customerReceiver);mockCustomerRepository.save(customerReceiver);` from the test doesn't make much sense. What are you trying to achieve? – JB Nizet Sep 11 '16 at 13:34
  • Thanks for the above answer. That did the trick! I wanted to after test this: `verify(mockCustomerRepository, times(2)).save(any());` – Deniss M. Sep 11 '16 at 13:36

2 Answers2

12

"Mock" your repository method calls. Also, use @InjectMocks instead @Autowired for TransactionService. And you can also use MockitoJUnitRunner. How to mock repository calls:

when(customerRepository.save(sender)).thenReturn(someSenderInstance);

To verify that mocked method call has been invoked use:

verify(customerRepository, times(1)).save(sender);

Also, remember one thing: You are testing services! Therefore, all calls to database should be mocked.

Branislav Lazic
  • 14,388
  • 8
  • 60
  • 85
  • why we need to use @ injectMock instead of @ Autowired with TransactionService ? – Umair May 13 '22 at 20:19
  • I have got the answer, and the answer is that you need to inject the mocks needed for repo class so for that you have to use @injectMocks with repo class. – Umair May 13 '22 at 20:33
1

As suggested by JB Nizet, just because you define a mock instance in a test doesn't mean all objects will start using that mock instance. To achieve the behaviour you want to achieve, you need to use @InjectMocks on the class you are testing which in your case is TransactionService. To understand the difference between @Mock and @InjectMocks , refer to this question difference between @Mock and @InjectMocks

Community
  • 1
  • 1
skm
  • 426
  • 8
  • 15