0

In tests i m trying to mock response from DB. Json: { description: "testDescription" contactPhone: "123456789" createDate: "18.04.2023 10:33:23" }

My POJO has field (OffsetDateTime createDate).

Here is my test

@ActiveProfiles("test")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = ServiceRequestApp.class)
@AutoConfigureMockMvc
class CreateUpdateSrControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @Autowired
    AbstractRestAdapter abstractRestAdapter;
    @MockBean
    SiebelDatabaseAdapter adapter;

    @Autowired
    GetSrService service;

    @Test
    void success_getSingleSR() throws Exception {
        DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                .appendPattern("dd.MM.yyyy HH:mm:ss")
                .toFormatter();
        JavaTimeModule module = new JavaTimeModule();
        module.addDeserializer(OffsetDateTime.class, new CustomDeserializer(formatter));

        ObjectMapper objectMapper = JsonMapper.builder()
                .configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false)
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
                .addModule(module)
                .build();

        ServiceRequest response = objectMapper.readValue(new File("src/test/resources/GET_response.json"), ServiceRequest.class);
        Mockito.when(adapter.getSingleSrV2("1-824E20I0", "1-631532350728")).thenReturn(response);

        mockMvc.perform(get("/service-request")
                        .contentType(MediaType.APPLICATION_JSON)
                        .param("serviceRequest.id", "1-824E20I0")
                        .param("serviceRequest.number", "1-631532350728")
                        .header("x-channel-id", "test"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.description").value("testDescription"))
                .andExpect(jsonPath("$.contactPhone").value("123456789"));
    }

    private class CustomDeserializer extends JsonDeserializer<OffsetDateTime> {

        private DateTimeFormatter formatter;

        public CustomDeserializer(DateTimeFormatter formatter) {
            this.formatter = formatter;
        }

        @Override
        public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
            return OffsetDateTime.parse(parser.getText(), this.formatter);
        }
    }

}

As you can see, i ve tried some methods covered on this site. But it doesnt work for me.

Here is my error:


com.fasterxml.jackson.databind.JsonMappingException: Text '18.04.2023 10:33:44' could not be parsed: Unable to obtain OffsetDateTime from TemporalAccessor: {},ISO resolved to 2023-04-18T10:33:44 of type java.time.format.Parsed (through reference chain: ru.mts.cx.support.service.model.rest.ServiceRequest["createDate"])

    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:392)
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:351)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1821)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:316)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
    at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3494)
    at ru.mts.cx.support.service.controller.CreateUpdateSrControllerTest.success_getSingleSR(CreateUpdateSrControllerTest.java:76)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
    at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
    at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
    at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.time.format.DateTimeParseException: Text '18.04.2023 10:33:44' could not be parsed: Unable to obtain OffsetDateTime from TemporalAccessor: {},ISO resolved to 2023-04-18T10:33:44 of type java.time.format.Parsed
    at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:2023)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1958)
    at java.base/java.time.OffsetDateTime.parse(OffsetDateTime.java:404)
    at ru.mts.cx.support.service.controller.CreateUpdateSrControllerTest$CustomDeserializer.deserialize(CreateUpdateSrControllerTest.java:100)
    at ru.mts.cx.support.service.controller.CreateUpdateSrControllerTest$CustomDeserializer.deserialize(CreateUpdateSrControllerTest.java:90)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:314)
    ... 74 more
Caused by: java.time.DateTimeException: Unable to obtain OffsetDateTime from TemporalAccessor: {},ISO resolved to 2023-04-18T10:33:44 of type java.time.format.Parsed
    at java.base/java.time.OffsetDateTime.from(OffsetDateTime.java:372)
    at java.base/java.time.format.Parsed.query(Parsed.java:241)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1954)
    ... 79 more
Caused by: java.time.DateTimeException: Unable to obtain ZoneOffset from TemporalAccessor: {},ISO resolved to 2023-04-18T10:33:44 of type java.time.format.Parsed
    at java.base/java.time.ZoneOffset.from(ZoneOffset.java:350)
    at java.base/java.time.OffsetDateTime.from(OffsetDateTime.java:361)
    ... 81 more

Tried to create CustomDeserializer, add DateTimeFormatter, add @JsonFormat(pattern = "dd.MM.yyyy HH:mm:ss") above my field type OffsetDateTime

Nik Korn
  • 21
  • 5
  • You are trying to use OffsetDateTime but not including the time zone / zone id. https://stackoverflow.com/questions/35298214/unable-to-obtain-offsetdatetime-from-temporalaccessor – geeves Apr 18 '23 at 16:05
  • Did you mean `LocalDateTime`? – g00se Apr 18 '23 at 16:19
  • @geeves your url leads to branch where manual transformation is discussed. But how can i solve this error with mapper? Btw, why (DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false) configuration doesnt solve zone id question? – Nik Korn Apr 18 '23 at 16:23
  • 1
    Maybe `.parseDefaulting(ChronoField.OFFSET_SECONDS, OffsetDateTime.now().getOffset().getTotalSeconds())` in your formatter creation. `java.time` mavens might know a less kludgy way – g00se Apr 18 '23 at 16:59
  • @g00se and that is the answer! It works perfectly and no need in changing Json and pattern format! Can you explain a bit, what your manipulation did? Because when i add "andExpect(jsonPath("$.createDate").value("2023-04-18T10:33" + OffsetDateTime.now().getOffset().getTotalSeconds()));" i have assertionError: expected:<2023-04-18T10:33:4410800> but was:<2023-04-18T10:33:44+03:00> – Nik Korn Apr 18 '23 at 17:29
  • Well, the point is, as I'm sure by now you know, if you want to have `OffsetDateTime` then you've got to supply a pattern that will take in the offset. OR, you tell the formatter to *default* to a certain offset. The code I posted simply uses the local default offset (as you said you wanted) – g00se Apr 18 '23 at 17:38
  • Which offset do you want? And no, @g00se, that is not exactly what we want. Using the current offset for the default time zone may be wrong, especially if the offset at the time in the string to be deserialized is different, which can easily be the case. – Ole V.V. Apr 18 '23 at 17:46
  • 1
    If you want to use the JVM’s current default time zone, I suggest `return LocalDateTime.parse(parser.getText(), this.formatter).atZone(ZoneId.systemDefault()).toOffsetDateTime();`. – Ole V.V. Apr 18 '23 at 17:48
  • Please for your next Stack Overflow questions train creating a [mre]. Your stack trace tells us that the problem is with the expression `OffsetDateTime.parse(parser.getText(), this.formatter)`, that is, all other code lines are not really relevant. Reproduce your problem in a small piece of code with that line only. It will make it a lot easier for everyone, increasing your chance of an answer that is good and to the point. – Ole V.V. Apr 18 '23 at 17:51
  • @OleV.V. *...especially if the offset at the time in the string to be deserialized is different, which can easily be the case* I thought the whole point is that the offset is *absent* - the whole reason for the problem. Nik please correct me if sometimes the offset *is* present – g00se Apr 18 '23 at 18:16
  • @g00se That’s why I asked *Which offset do you want?* The OP needs a UTC offset for an `OffsetDateTime` (hence the name). I can’t guess which, and if you can, you have abilities beyond what your (very qualified) contributions to Stack Overflow have shown. ;-) – Ole V.V. Apr 18 '23 at 18:22
  • @g00se you get it absolutely right. Thank you one more time! – Nik Korn Apr 18 '23 at 18:24
  • @OleV.V. thank you for your feedback. Next time i ll try to be more certain and sharp. – Nik Korn Apr 18 '23 at 18:25
  • @OleV.V *The OP needs a UTC offset for an OffsetDateTime (hence the name). I can’t guess which* No need to guess ;) it says right there in his code: *DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE* IOW, use the default offset – g00se Apr 18 '23 at 19:12

1 Answers1

0

The type of the field createDate is OffsetDateTime. To test it your test data must contain the offset. Even if it is equal to 0. Otherwise it can't be parsed.

Solution

Adjust the formatter to contain a 'Z'

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendPattern("dd.MM.yyyy HH:mm:ssZ")
            .toFormatter();

Add offset to the test data

createDate: "18.04.2023 10:33:23+0000"
Mar-Z
  • 2,660
  • 2
  • 4
  • 16
  • And this variant is working fine, thank you! But what if in DB this field doesnt contain +0000? The type of data is DATE in DB. – Nik Korn Apr 18 '23 at 17:14
  • If it comes without offset then your application (not only the test) should not use OffsetDateTime in the POJO. Other candidate would be LocalDateTime. However in that case the DB and application must be configured to use the same time zone. – Mar-Z Apr 18 '23 at 17:25
  • not sure, but this date format is what i get from DB. RawMapper while parsing resultSet doing ok with it. I think it works like we discussed in the comments below my question (thanks g00se). I suppose rawMapper just adding offset by default. – Nik Korn Apr 18 '23 at 18:34