Mockito异常:抛出正确IOException仍提示Checked exception无效的问题
解决Mockito模拟ObjectMapper.readValue抛出IOException时的异常问题
问题描述
我在测试DecileConfigurationJsonConverter类的数据库数据读取异常场景时遇到了问题:
- 该类实现了
AttributeConverter<List<DecileParameter>, String>接口,convertToEntityAttribute方法会捕获IOException并返回null; - 用Mockito模拟
ObjectMapper的readValue方法抛出java.io.IOException时,触发org.mockito.exceptions.base.MockitoException,提示“Checked exception is invalid for this method!”。
相关代码
转换器类
public class DecileConfigurationJsonConverter implements AttributeConverter<List<DecileParameter>, String> { private static final ObjectMapper objectMapper = new ObjectMapper(); @Override public List<DecileParameter> convertToEntityAttribute(String dbData) { if (dbData == null) return null; List<DecileParameter> dep = null; try { dep = objectMapper.readValue(dbData, new TypeReference<List<DecileParameter>>() {}); } catch (final IOException e) { return null; } return dep; } // 此处省略convertToDatabaseColumn方法实现 }
测试类
@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc @ActiveProfiles("dev-test") @TestPropertySource(locations = "classpath:application-dev-test.yml") @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class UtilPackageTestMore { @Mock ObjectMapper objectMapper; private static void setFinalStaticField(Class<?> clazz, String fieldName, Object value) throws ReflectiveOperationException { Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); Field modifiers = Field.class.getDeclaredField("modifiers"); modifiers.setAccessible(true); modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL); field.set(null, value); } @Test public void TestDecileConfigurationJsonConverterExceptions() throws Exception { DecileConfigurationJsonConverter dc = new DecileConfigurationJsonConverter(); ObjectMapper objecmapper = Mockito.mock(ObjectMapper.class); setFinalStaticField(DecileConfigurationJsonConverter.class, "objectMapper", objecmapper); List<DecileParameter> attribute = new ArrayList<>(); attribute.add(new DecileParameter("1","2","3","4","5")); Mockito.when(objecmapper.writeValueAsString(attribute)).thenThrow(JsonProcessingException.class); String res = dc.convertToDatabaseColumn(attribute); assertNull(res); // 此处触发异常 Mockito.when(objecmapper.readValue("db data",new TypeReference<List<DecileParameter>>())).thenThrow(java.io.IOException.class); List<DecileParameter> convertToEntityAttribute = dc.convertToEntityAttribute("db data"); assertNull(convertToEntityAttribute); } }
问题原因
Mockito禁止模拟方法抛出该方法声明中未定义的检查型异常。查看ObjectMapper.readValue的方法签名,它实际声明抛出的是JsonProcessingException(IOException的子类),而非直接的IOException,因此直接抛出IOException会被判定为无效操作。
解决方法
1. 使用正确的异常类型模拟
将抛出的异常改为JsonProcessingException(或其子类如JsonParseException),这是readValue方法允许抛出的检查型异常:
// 修改测试中的异常抛出代码 Mockito.when(objecmapper.readValue("db data", new TypeReference<List<DecileParameter>>())).thenThrow(JsonProcessingException.class); List<DecileParameter> convertToEntityAttribute = dc.convertToEntityAttribute("db data"); assertNull(convertToEntityAttribute);
如果需要更贴合真实解析失败场景,也可以使用具体的异常子类:
Mockito.when(objecmapper.readValue("db data", new TypeReference<List<DecileParameter>>())) .thenThrow(new JsonParseException("JSON解析失败", null));
2. 优化转换器类的可测试性(推荐)
通过构造函数注入ObjectMapper,避免使用反射修改静态final字段的繁琐操作,让测试更优雅:
public class DecileConfigurationJsonConverter implements AttributeConverter<List<DecileParameter>, String> { private final ObjectMapper objectMapper; // 构造函数注入,供测试使用 public DecileConfigurationJsonConverter(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } // 无参构造函数,兼容Spring自动装配 public DecileConfigurationJsonConverter() { this(new ObjectMapper()); } @Override public List<DecileParameter> convertToEntityAttribute(String dbData) { if (dbData == null) return null; try { return objectMapper.readValue(dbData, new TypeReference<List<DecileParameter>>() {}); } catch (final IOException e) { return null; } } // 实现convertToDatabaseColumn方法... }
修改后的测试代码无需反射,直接传入Mock对象:
@Test public void TestDecileConfigurationJsonConverterExceptions() throws Exception { ObjectMapper mockMapper = Mockito.mock(ObjectMapper.class); DecileConfigurationJsonConverter dc = new DecileConfigurationJsonConverter(mockMapper); List<DecileParameter> attribute = new ArrayList<>(); attribute.add(new DecileParameter("1","2","3","4","5")); Mockito.when(mockMapper.writeValueAsString(attribute)).thenThrow(JsonProcessingException.class); String res = dc.convertToDatabaseColumn(attribute); assertNull(res); Mockito.when(mockMapper.readValue("db data", new TypeReference<List<DecileParameter>>())).thenThrow(JsonProcessingException.class); List<DecileParameter> convertToEntityAttribute = dc.convertToEntityAttribute("db data"); assertNull(convertToEntityAttribute); }
内容的提问来源于stack exchange,提问作者PAMPA ROY
相关产品推荐
相关产品推荐

