如何通过JUnit测试覆盖Jackson重写toString方法的异常分支?
如何用JUnit覆盖Jackson重写toString的catch分支?
最近我用Jackson重写了类的toString方法,把对象序列化成JSON字符串返回,代码是这样的:
@Override public String toString() { ObjectMapper mapper = new ObjectMapper(); try { return mapper.writeValueAsString(this); } catch (JsonProcessingException e) { logger.error(e.getMessage()); return ""; } }
但编写JUnit测试时,我发现正常的测试用例只能覆盖try里的正常分支,catch块始终没被覆盖到,当前的测试代码如下:
@Test public void testToString() { TestClass testClass = new TestClass(); String expected = "{\"testAttr\":null}"; assertEquals(expected, testClass.toString()); }
那怎么才能覆盖到catch块呢?其实核心就是让Jackson的writeValueAsString方法抛出JsonProcessingException,这里有两种实用的方法:
方法一:构造无法被Jackson序列化的对象
最简单的方式就是给测试类添加一个Jackson无法处理的字段,让序列化过程失败。比如:
- 先定义一个Jackson默认无法序列化的类(比如没有无参构造器,也没加任何Jackson序列化注解):
class UnserializableClass { public UnserializableClass(String value) { // 只有带参构造,没有无参构造 } }
- 在你的
TestClass里添加这个类型的字段:
public class TestClass { private String testAttr; // 添加这个无法序列化的字段 private UnserializableClass badField = new UnserializableClass("test"); // 其他代码... // 你重写的toString方法... }
- 编写测试用例:
@Test public void testToStringWhenSerializationFails() { TestClass testClass = new TestClass(); // 序列化失败会进入catch块,返回空字符串 assertEquals("", testClass.toString()); }
这种方法的好处是简单直接,不需要额外的测试依赖,缺点是需要修改测试用的类实例结构。
方法二:用Mock工具模拟ObjectMapper抛出异常
如果不想改动原类的结构,更优雅的方式是用Mockito或PowerMock来mockObjectMapper,让它在调用writeValueAsString时抛出异常。这里以PowerMock为例:
- 首先确保你的项目引入了PowerMock和Mockito的依赖。
- 编写测试用例:
import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @RunWith(PowerMockRunner.class) @PrepareForTest(TestClass.class) // 指定要mock的类所在的类 public class TestClassTest { @Test public void testToStringWithSerializationException() throws Exception { // 1. Mock一个ObjectMapper实例 ObjectMapper mockMapper = Mockito.mock(ObjectMapper.class); // 2. 让这个mock实例调用writeValueAsString时抛出异常 Mockito.when(mockMapper.writeValueAsString(Mockito.any(TestClass.class))) .thenThrow(new JsonProcessingException("Test serialization error") {}); // 3. 替换TestClass里new出来的ObjectMapper为我们的mock实例 PowerMockito.whenNew(ObjectMapper.class).withNoArguments().thenReturn(mockMapper); // 4. 执行测试,验证返回空字符串 TestClass testClass = new TestClass(); assertEquals("", testClass.toString()); // 可选:验证logger.error是否被调用 // Mockito.verify(logger).error(Mockito.anyString()); } }
这种方法的优势是完全隔离了测试环境,不需要修改原类的结构,符合单元测试的隔离原则,缺点是需要引入额外的测试依赖,配置稍复杂。
不管用哪种方法,都能让代码进入catch块,从而实现100%的代码覆盖率。
内容的提问来源于stack exchange,提问作者Unai Fifo
相关产品推荐
相关产品推荐

