使用JUnit 5和Mockito时服务层DAO对象无法被Mock
服务层代码
@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); // 返回空列表 Map<String, String> recordsMap = new HashMap<>(); for (Map.Entry<String, Object> entry : records.get(0).entrySet()) { recordsMap.put(entry.getKey(), entry.getValue() != null ? entry.getValue().toString() : ""); } return recordsMap; } }
测试类代码
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); // 因空列表报错 assertNotNull(actualDetails); assertEquals(expectedDetails, actualDetails); verify(xyzDaoDao, times(1)).getRecords(xyzRoot, allParams); // 变量名拼写错误 } }
问题根源及解决方案
1. 参数匹配失败(核心问题)
Mockito默认通过对象的equals()方法匹配参数。测试类中手动创建的xyzRoot,和服务层里通过ObjectMapper反序列化生成的xyzRoot是两个不同的对象,如果XyzRoot类未正确重写equals()方法,两者匹配失败,导致预设的Stub逻辑不生效,DAO返回默认的空列表。
解决方案:
使用Mockito参数匹配器绕过严格的对象相等校验:
// 导入匹配器 import static org.mockito.ArgumentMatchers.*; // 方式1:用refEq通过反射匹配对象字段(忽略hashCode/equals实现) when(xyzDao.getRecords(refEq(xyzRoot), eq(allParams))).thenReturn(recordDetails); // 方式2:用any()忽略XyzRoot参数的匹配(适合不需要校验该参数的场景) when(xyzDao.getRecords(any(XyzRoot.class), eq(allParams))).thenReturn(recordDetails);
2. 变量名拼写错误
测试类的verify语句中,误写为xyzDaoDao,但实际Mock的变量是xyzDao,会导致校验时报错,需修正:
verify(xyzDao, times(1)).getRecords(any(XyzRoot.class), eq(allParams));
3. 可选优化:统一对象创建逻辑
如果需要严格校验XyzRoot参数,可在测试类中复用服务层的对象创建方式,保证参数对象一致:
String config = gson.toJson(xyzRoot); System.setProperty("key", config); // 和服务层一样用ObjectMapper反序列化生成XyzRoot XyzRoot mockXyzRoot = new ObjectMapper().readValue(config, XyzRoot.class); when(xyzDao.getRecords(mockXyzRoot, allParams)).thenReturn(recordDetails);
内容的提问来源于stack exchange,提问作者Joginder Pawan Kumar
相关产品推荐
相关产品推荐

