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

如何通过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无法处理的字段,让序列化过程失败。比如:

  1. 先定义一个Jackson默认无法序列化的类(比如没有无参构造器,也没加任何Jackson序列化注解):
class UnserializableClass {
    public UnserializableClass(String value) {
        // 只有带参构造,没有无参构造
    }
}
  1. 在你的TestClass里添加这个类型的字段:
public class TestClass {
    private String testAttr;
    // 添加这个无法序列化的字段
    private UnserializableClass badField = new UnserializableClass("test");
    
    // 其他代码...
    // 你重写的toString方法...
}
  1. 编写测试用例:
@Test
public void testToStringWhenSerializationFails() {
    TestClass testClass = new TestClass();
    // 序列化失败会进入catch块,返回空字符串
    assertEquals("", testClass.toString());
}

这种方法的好处是简单直接,不需要额外的测试依赖,缺点是需要修改测试用的类实例结构。

方法二:用Mock工具模拟ObjectMapper抛出异常

如果不想改动原类的结构,更优雅的方式是用Mockito或PowerMock来mockObjectMapper,让它在调用writeValueAsString时抛出异常。这里以PowerMock为例:

  1. 首先确保你的项目引入了PowerMock和Mockito的依赖。
  2. 编写测试用例:
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:24:20