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

为何Mockito测试中,ZonedDateTime结合Dozer会抛NoSuchMethodException?

问题:DozerBeanMapper在JUnit测试中映射Java 8时间字段抛出NoSuchMethodException异常

我近期编写JUnit测试用例时遇到问题:待测试方法使用DozerBeanMapper复制字段,我使用@Spy而非@Mock注入DozerBeanMapper(@Mock生成的假对象会导致断言异常)。

第一种实现方式(执行测试抛出异常)

class A {
  @Autowired
  DozerBeanMapper mapper;

  Entity method(Request req) {
      Entity ent = new Entity();
    
      req.setCreatedOn(ZonedDateTime.now());
      mapper.map(req, ent);
    
      return ent;
  }
}

抛出的异常信息

org.dozer.MappingException: java.lang.NoSuchMethodException: java.time.LocalDateTime.<init>()
at org.dozer.util.MappingUtils.throwMappingException(MappingUtils.java:82) ~[dozer-5.4.0.jar:na]
at org.dozer.factory.ConstructionStrategies$ByConstructor.newInstance(ConstructionStrategies.java:261) ~[dozer-5.4.0.jar:na]
at org.dozer.factory.ConstructionStrategies$ByConstructor.create(ConstructionStrategies.java:245) ~[dozer-5.4.0.jar:na]
at org.dozer.factory.DestBeanCreator.create(DestBeanCreator.java:65) ~[dozer-5.4.0.jar:na]
at org.dozer.MappingProcessor.mapCustomObject(MappingProcessor.java:489) [dozer-5.4.0.jar:na]
at org.dozer.MappingProcessor.mapOrRecurseObject(MappingProcessor.java:446) [dozer-5.4.0.jar:na]
at org.dozer.MappingProcessor.mapFromFieldMap(MappingProcessor.java:342) [dozer-5.4.0.jar:na]
at org.dozer.MappingProcessor.mapField(MappingProcessor.java:288) [dozer-5.4.0.jar:na]

调整后的实现方式(运行正常)

class A {
    @Autowired
    DozerBeanMapper mapper;

    Entity method(Request req){
      Entity ent = new Entity();
    
      mapper.map(req, ent);
      ent.setCreatedOn(ZonedDateTime.now());

      return ent;
  }
}

测试用例代码

class ATest {
    @InjectMocks
    A aService;

    @Spy
    DozerBeanMapper mapper;
    
    @Test
    void methodTest(){
      aService.method(req);
  }
}

异常原因分析

核心原因是你使用的Dozer 5.4.0版本不支持Java 8的时间API(如ZonedDateTime、LocalDateTime)。

第一种写法中,你先给req设置了ZonedDateTime类型的createdOn字段,随后调用mapper.map(req, ent)时,Dozer在处理这个时间字段的映射逻辑时,会尝试通过无参构造实例化LocalDateTime(可能是因为Entity的createdOn字段类型为LocalDateTime,或者Dozer内部类型转换逻辑的问题)。但Java 8的时间类(包括LocalDateTime)都没有公共的无参构造方法,只能通过静态工厂方法创建实例,因此触发了NoSuchMethodException,最终包装为MappingException抛出。

而第二种写法中,你先执行Dozer映射(此时req的createdOn字段未被赋值,或Dozer映射时跳过了该字段),之后手动给ent设置ZonedDateTime值,绕开了Dozer对Java 8时间字段的映射逻辑,因此不会触发异常。

另外,你用@Spy注入的是真实的DozerBeanMapper实例,它会执行完整的真实映射逻辑,所以会暴露这个兼容性问题;而@Mock生成的是假对象,不会执行真实的字段映射逻辑,因此不会报错,但会导致后续断言失败(因为目标对象的字段未被正确复制)。

解决建议

  • 升级Dozer版本到支持Java 8时间API的版本(如Dozer 6.x及以上,或者引入dozer-jdk8扩展包)
  • 保持第二种写法,手动处理Java 8时间字段的赋值,避免通过Dozer映射这类字段
  • 在Dozer配置中添加自定义的Java 8时间类型转换器,覆盖默认的映射逻辑

内容的提问来源于stack exchange,提问作者Sachi97

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 07:05:19