Search code examples
javamockitodozerzoneddatetimespringmockito

Why ZonedDateTime.now() throws NoSuchMethodException while running test cases(using mockito)


Lately I am writing junit test cases and I came across ZonedDateTime.now() works partially expected where the method which we try to write test case uses DozerBeanMapper for copying common fields. Please go through below to have better understanding btw I am using @Spy for DozerBeanMapper not @Mock because @Mock provides me dummy Object which troubles while I assert at the end.

class A {
  @Autowired
  DozerBeanMapper mapper;

  Entity method(Request req) {

      Entity ent = new Entity();
    
      req.setCreatedOn(ZonedDateTime.now());
      dozerMapper.map(req,ent);
    
      return ent;

  }
}

Throws exception for above code :

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]

But It works absolute fine when I alter and add setCreatedOn after dozerMapper maps like below and I know this one is the correct way.

Class A {
    @Autowired
    DozerBeanMapper mapper;

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

      return ent;
  }
}

My whole point is why it does not work for first method code?

Here is the sudo test method

Class ATest {
    @InjectMocks
    A aService;

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

I want an brief explanation why first code does not work?


Solution

  • Because dozer is attempting to create an instance of ZonedDateTime without knowing how, so it applies the usual strategy:

    • Call the no-args constructor, find set methods to invoke.
    • Call the no-args constructor, set the fields directly.
    • Find a constructor that takes 1 of each arg in turn.

    And ZonedDateTime has none of those - the 'formula' to make one is to call .of(...) or now() or parse(...) - hence, failure.

    That exception means: "I tried to invoke new ZonedDateTime() but this did not work, as that constructor does not exist". At the bytecode/JVM level, constructors are, effectively, public static OwnType <init>(args) {} - they are 'methods' whose name is <init>. Hence, java.lang.NoSuchMethodException: java.time.LocalDateTime.<init>().

    This is thrown by dozer's mapper infrastructure: It is trying to take some data from some source and 'map that' into an instance of ZonedDateTime.

    Check the dozer documentation on how to provide to provide it the functions it needs to do this mapping. Perhaps this gist is what you need.