如何用Mockito测试Worker类:Mock无返回值change方法并验证逻辑
Worker类Mock测试与可测试性优化解答
首先先修正你提供的代码语法问题,方便后续理解:
class Worker { public Response work() { // 这里初始化entry和map参数 Map.Entry<String, String> entry = Map.entry("demoKey", "demoValue"); Map<String, String> map = new HashMap<>(); Helper h = new Helper(); h.change(entry, map); // 使用修改后的map构建response并返回 Response response = new Response(map); return response; } } class Helper { public void change(Map.Entry<?, ?> entry, Map<?, ?> targetMap) { // 向目标map添加键值对 targetMap.put(entry.getKey(), entry.getValue()); } } // 假设Response类定义 class Response { private final Map<?, ?> data; public Response(Map<?, ?> data) { this.data = data; } public Map<?, ?> getData() { return data; } }
1. 如何编写Worker的测试类
要mock Helper的change方法并测试work的完整逻辑,核心是解耦Worker与Helper的依赖——原代码中Worker直接new Helper,导致无法替换为mock实例。具体步骤如下:
第一步:重构Worker,支持依赖注入
修改Worker,通过构造方法注入Helper,让测试时能传入mock实例:
class Worker { private final Helper helper; // 构造注入Helper,解耦依赖 public Worker(Helper helper) { this.helper = helper; } public Response work() { Map.Entry<String, String> entry = Map.entry("demoKey", "demoValue"); Map<String, String> map = new HashMap<>(); helper.change(entry, map); return new Response(map); } }
第二步:编写Mockito测试类
使用Mockito创建Helper的mock实例,注入Worker后,通过doAnswer()自定义change方法的行为,同时验证完整逻辑:
import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import java.util.HashMap; import java.util.Map; import static org.mockito.Mockito.*; import static org.junit.Assert.*; @RunWith(MockitoJUnitRunner.class) public class WorkerTest { @Mock private Helper mockHelper; @InjectMocks private Worker worker; @Test public void testWork() { // 1. 准备测试用的entry Map.Entry<String, String> testEntry = Map.entry("testKey", "testValue"); // 用于接收修改后的map内容 Map<String, String> capturedMap = new HashMap<>(); // 2. 自定义mockHelper.change的行为 doAnswer(invocation -> { // 获取方法传入的参数 Map.Entry<?, ?> entry = invocation.getArgument(0); Map<?, ?> map = invocation.getArgument(1); // 模拟修改map的逻辑(替代真实Helper的行为) map.put(entry.getKey(), entry.getValue()); // 将修改后的内容同步到测试类的变量,方便外部断言 capturedMap.putAll((Map<? extends String, ? extends String>) map); return null; }).when(mockHelper).change(any(Map.Entry.class), any(Map.class)); // 3. 调用被测试方法 Response response = worker.work(); // 4. 断言验证 // 验证map是否被正确修改 assertEquals("testValue", capturedMap.get("testKey")); // 验证response是否基于修改后的map构建 assertEquals("testValue", response.getData().get("testKey")); // 验证mockHelper的change方法是否被调用 verify(mockHelper, times(1)).change(any(Map.Entry.class), any(Map.class)); } }
2. 能否在doAnswer()外部使用Mock修改后的参数?
可以。因为Map是引用类型,只要在doAnswer()中对传入的map实例进行修改,测试类中如果持有同一个实例(或者通过doAnswer()把修改后的内容同步到测试类的变量中),就能在外部访问到修改后的结果。
比如上面的测试代码中,我们在doAnswer()里把修改后的map内容同步到测试类的capturedMap变量,之后就能直接在外部断言这个变量的内容。
3. 是否需要重构代码提升可测试性?
必须重构。原代码存在以下影响可测试性的问题:
- Worker直接在方法内部
new Helper,导致两者耦合度极高,无法替换Helper为mock实例,无法单独测试Worker的业务逻辑; - Helper的
change方法参数命名(原代码的empty)和泛型定义不清晰,容易引发误解; - Worker内部硬编码entry和map的创建逻辑,无法灵活传入测试数据。
除了依赖注入Helper,还可以做以下优化:
- 明确泛型类型,避免使用无界通配符
<?, ?>; - 如果entry和map是外部输入,可改为将它们作为
work()方法的参数,进一步提升测试灵活性; - 给Helper的
change方法更清晰的命名,比如addEntryToMap。
内容的提问来源于stack exchange,提问作者Shreya Mukherjee
相关产品推荐
相关产品推荐

