Test Spring-Boot Repository interface methods without touching the database using Mockito
Solution 1
"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.
Solution 2
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
Deniss M.
I'm just a newb trying to get it together. It is quite hard to do it in a sense that I needs some time to do it.
Updated on July 06, 2022Comments
-
Deniss M. over 1 year
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?
-
Johon smuthio over 1 yearwhy we need to use @ injectMock instead of @ Autowired with TransactionService ?
-
Johon smuthio over 1 yearI 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.