@InjectMocks and @MockConsider a new class Store.java and we want to test the getMaleCashiers() method:
/* Store.java */
public class Store() {
private String id;
@Autowired
private CashierServiceImpl cashierService;
public Store(String id) {
this.id = id;
}
public List<Cashier> getMaleCashiers() throws CacheException {
// Get list of cashiers by store id from cache or database
List<Cashier> cashierList = cashierService.getByStoreId(this.id);
// Filter and return list of male cashiers
return cashierList
.stream()
.filter(c -> c.getGender().equalsIgnoreCase('male'))
.collect(Collectors.toList()));
}
}
Note that Store has a private member variable CashierServiceImpl which is initialized through Spring’s dependency injection framework using @Autowired.
We start by creating StoreTest.java which will contain our JUnit test. The test class is annotated with @RunWith(SpringJUnit4ClassRunner.class):
/* StoreTest.java */
@RunWith(SpringJUnit4ClassRunner.class)
public class StoreTest() {
// TODO: Initialize objects
// TODO: Write test cases
}
@InjectMocks and @MockWe declare the member variables store and cashierServiceImpl in the test class.
By annotating store with @InjectMocks and cashierServiceImpl with @Mock, store will have a mock instance of cashierServiceImpl instead of null.
Finally, we also need to call MockitoAnnotations.initMocks(this) to initialize the mock variables before the test is run. One way to do this is by calling it in a method annotated with @Before:
/* StoreTest.java */
@RunWith(SpringJUnit4ClassRunner.class)
public class StoreTest() {
@InjectMocks
Store store;
@Mock
CashierServiceImpl cashierService;
@Before
public void initMocks() {
// Initialize mock variables
MockitoAnnotations.initMocks(this);
}
}
Let’s now write the test for the store.getMaleCashiers(). To recap:
/* Store.java */
public class Store {
// ...
public List<Cashier> getMaleCashiers() throws CacheException {
// Get list of cashiers by store id from cache or database
List<Cashier> cashierList = cashierService.getByStoreId(this.id);
// Return male cashiers
return cashierList
.stream()
.filter(c -> c.getGender().equalsIgnoreCase('male'))
.collect(Collectors.toList()));
}
}
cashierService.getByStoreId(this.id) retrieves the list of cashiers associated with the given store id from either the cache or database.
For our test on store.getMaleCashers():
cashierService.getByStoreId(this.id)cashierService.getByStoreId(this.id) does not need to retrieve the result from an actual cache or database when we run the testAs such, we can use Mockito’s when and thenReturn methods to mock the result when cashierService.getByStoreId(this.id) is being called during testing.
What we are concerned with is the following line that filters cashierList by male cashiers and returns the correct result.
As such, here we have the complete code for our test:
/* StoreTest.java */
@RunWith(SpringJUnit4ClassRunner.class)
public class StoreTest() {
@InjectMocks
Store store;
@Mock
CashierServiceImpl cashierService;
@Before
public void initMocks() {
// Initialize mock variables
MockitoAnnotations.initMocks(this);
}
@Test
public void getMaleCashiersTest_shouldReturnOne() throws CacheException {
// CashierList
List<Cashier> cashierList = new ArrayList<>();
Cashier maleCashier = new Cashier();
maleCashier.setGender("male");
cashierList.add(maleCashier);
Cashier femaleCashier = new Cashier();
femaleCashier.setGender("female");
cashierList.add(femaleCashier);
// Mock getByStoreId(this.id) to return cashierList that we have created
when(cashierService.getByStoreId(any())).thenReturn(cashierList);
// Test and assert
List<Cashier> result = store.getMaleCashiers();
assertEquals(1, result.size());
}
}
Store instanceIn the above example, we have seen how we can initialize Store using @InjectMocks and @Mocks.
There are also other ways to initialize Store in the test class:
Store instance@Autowired on store member variableStore instance@RunWith(SpringJUnit4ClassRunner.class)
public void StoreTest() {
Store store;
public StoreTest() {
this.store = new Store("Cheers");
}
// @Test
// ...
}
The issue with manually initializing is that you may not be able to initialize the private member variables of Store:
Store does not take in an instance of CashierServiceImpl in its constructorCashierServiceImpl is a private member variable which is initialized by Spring’s dependency injection framework using @Autowired@Autowired on store member variableWell, you may then consider to annotate Store with Autowired instead and let Spring do its own dependency injection magic thingy. However it works because we don’t really care…do we?
@RunWith(SpringJUnit4ClassRunner.class)
public void StoreTest() {
@Autowired
Store store;
// @Test
// ...
}
Unfortunately, you see errors in your console log when you try to run StoreTest.
Resolving these errors may or may not be straightforward, depending on how complex the member variables are. You will need to declare @ComponentScan so that the autowired variables can be initialized when the test is run.
On top of that, component scanning can result in longer compile time when running the test file, especially for a large codebase.