Issue
I am writing test case for service layer using JUnit 5 and Mockito. I am mocking database layer using @Mock
and injecting into service layer using @InjectMocks
. But, when call goes to service method, somehow mocked list from DAO is coming as empty. I am having similar kind of set up for other test classes and it is working fine. I even tried in same class by creating a simple flow which accepts a string argument and returning a string object and it worked. But somehow for this method, its not working for me. While debugging, I checked parameters are being passed as expected, its just DAO layer is giving empty list even after mocking it. Please let me know what wrong I am doing here.
Service Layer
@Service
public class XyzServiceImpl implements XyzService {
@Autowired
private XyzDAO xyzDAO;
@Override
public Map<String, String> getRecords(Map<String, String> allParams) throws Exception {
String key = allParams.get("key");
String configValue = System.getProperty(key);
XyzRoot xyzRoot = new ObjectMapper().readValue(configValue, XyzRoot.class);
List<Map<String, Object>> records = xyzDao.getRecords(xyzRoot, allParams); // list is coming as empty
for (Entry<String, Object> entry : records.get(0).entrySet()) {
recordsMap.put(entry.getKey(), entry.getValue()!= null ? entry.getValue().toString() : "");
}
return recordsMap;
}
}
Here is the code for test class
public class TestXyzService {
@InjectMocks
private XyzServiceImpl xyzServiceImpl;
@Mock
private xyzDAO xyzDao;
private static String data = null;
@BeforeEach
public void init() {
MockitoAnnotations.initMocks(this);
}
@BeforeAll
public static void setUp() throws IOException {
data = FileUtils.loadFileData("record-history.json");
}
@Test
void getRecordTest() throws Exception {
Gson gson = new Gson();
Map<String, String> expectedDetails = gson.fromJson(data,
new TypeToken<Map<String, String>>() {
}.getType());
Map<String, Object> recordDetailsMap = gson.fromJson(data,
new TypeToken<Map<String, Object>>() {
}.getType());
List<Map<String, Object>> recordDetails = new ArrayList<>();
recordDetails.add(recordDetailsMap);
Map<String, String> allParams = new LinkedHashMap<>();
allParams.put(AppConstants.PARAM_PAGE_NAME, "HISTORY_TBL");
allParams.put(AppConstants.PARAM_ARG1, AppConstants.ARG1);
XyzRoot xyzRoot = new XyzRoot();
xyzRoot.setTable("TEST_TBL");
Configuration configuration = new Configuration();
configuration.setArgument("COL");
xyzRoot.setConfig(configuration);
String config = gson.toJson(xyzRoot);
System.setProperty("key", config);
when(xyzDao.getRecords(xyzRoot, allParams)).thenReturn(recordDetails);
Map<String, String> actualDetails = xyzServiceImpl.getRecords(allParams); // getting error due to empty list from dao
assertNotNull(actualDetails);
assertEquals(expectedDetails, actualDetails);
verify(xyzDaoDao, times(1)).getRecords(xyzRoot, allParams);
}
}
Solution
The object created by ObjectMapper
in this line:
XyzRoot xyzRoot = new ObjectMapper().readValue(configValue, XyzRoot.class);
is an instance that is completely separated from the instance you're creating in the test:
XyzRoot xyzRoot = new XyzRoot();
xyzRoot.setTable("TEST_TBL");
You do not have an equals
method implemented for XyzRoot
, so simple reference equality verification (==
), which is a default Object
implementation inherited by all classes, returns false
as the objects are two completely separate instances. That's why when(...).thenReturn(...)
defined in your test is not working properly - when Mockito checks if it should fire for given object, it uses equals
method by default.
To solve the problem, you should do one of the following:
- define
equals
andhashCode
forXyzRoot
(remember about the contract) - use argThat argumentMatcher
- use refEq argument matcher
Answered By - Jonasz
Answer Checked By - Gilberto Lyons (JavaFixing Admin)