JMockit迁移Mockito:枚举常量方法调用次数验证改造
枚举方法调用次数测试:从JMockit迁移至Mockito
需求背景
原有基于JMockit的测试用例,用于统计HyperlinkActionType枚举中performActionIfApplicable方法的调用次数,预期仅EDIT枚举常量会触发该方法,调用次数为1。由于Java17环境限制,需要将测试从JMockit迁移至Mockito,且需通过对每个枚举值进行spy的方式实现。
原枚举代码(HyperlinkActionType.java)
public enum HyperlinkActionType { EDIT("Edit") { @Override public void invokeRequest(String entityId) { performActionIfApplicable(entityId); } }, DONOTHING("Donothing") { @Override public void invokeRequest(String entityId) { // 空实现 } }; public void invokeRequest(String entityId) { // 默认实现 } void performActionIfApplicable(String entityId) { // 原有业务逻辑 } }
原JMockit测试代码
public void testActionsThatNeedVerifyingPermissionsBeforeExecution() { IntegerWrapper value = new IntegerWrapper(0); new MockUp<HyperlinkActionType>() { @SuppressWarnings("ParameterHidesMemberVariable") @Mock void performActionIfApplicable(String entityId) { value.increment(); } }; // 遍历所有枚举值并调用invokeRequest Arrays.stream(HyperlinkActionType.values()).forEach(action -> action.invokeRequest(null)); Assert.assertEquals("仅EDIT操作应触发performActionIfApplicable方法", 1, value.getWrappedObject().intValue()); }
迁移后的Mockito测试代码
import org.junit.Test; import org.mockito.Mockito; import java.util.Arrays; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class HyperlinkActionTypeTest { @Test public void testActionsThatNeedVerifyingPermissionsBeforeExecution() { // 方式1:用Mockito原生verify验证调用次数(推荐) HyperlinkActionType[] spiedEnums = Arrays.stream(HyperlinkActionType.values()) .map(Mockito::spy) .toArray(HyperlinkActionType[]::new); // 调用所有枚举实例的invokeRequest Arrays.stream(spiedEnums).forEach(action -> action.invokeRequest(null)); // 逐个验证方法调用次数 for (HyperlinkActionType spiedAction : spiedEnums) { if (spiedAction == HyperlinkActionType.EDIT) { verify(spiedAction, times(1)).performActionIfApplicable(Mockito.anyString()); } else { verify(spiedAction, times(0)).performActionIfApplicable(Mockito.anyString()); } } // 方式2:沿用原JMockit的计数器逻辑(和旧测试对齐) /* AtomicInteger callCount = new AtomicInteger(0); Arrays.stream(HyperlinkActionType.values()) .map(Mockito::spy) .forEach(spiedAction -> { // 拦截performActionIfApplicable方法并计数 Mockito.doAnswer(invocation -> { callCount.incrementAndGet(); return null; }).when(spiedAction).performActionIfApplicable(Mockito.anyString()); spiedAction.invokeRequest(null); }); assertEquals("仅EDIT操作应触发目标方法", 1, callCount.get()); */ } }
迁移关键说明
- 枚举Spy的必要性:枚举实例是天然单例且不可变,Mockito无法直接mock整个枚举类,必须对每个枚举实例单独创建spy对象,保留原有业务逻辑的同时记录方法调用行为。
- Java17适配:Mockito的spy方式无需修改枚举类结构,避开了JMockit在Java17模块化环境下的反射兼容性问题。
- 两种验证逻辑:既可以用Mockito原生的
verify方法直接验证调用次数,也可以沿用原测试的计数器逻辑,按需选择即可。
内容的提问来源于stack exchange,提问作者Margub Khan
相关产品推荐
相关产品推荐

