You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 12:25:22