Search code examples
javaspringspring-bootlocaldatecontract

Spring Cloud Contract LocalDateTime assertions fail


for the last day i've been struggling with the LocalDateTime in Spring Contract Tests.

base class:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
public abstract class VennotenDossiersConsumerBase {

@Autowired
private OptInController optInController;

@Autowired
private ActieLogController actieLogController;

@MockBean
private OptInService optInService;

@MockBean
private ActieLogService actieLogService;

@BeforeEach
public void setup() throws IOException {
    String liantisId = "liantisId";
    String applicatieNaam = "applicatieNaam";
    String gebruikerId = "gebruikerId";
    String optInGebruikerId = "optInGebruikerId";

    StandaloneMockMvcBuilder standaloneMockMvcBuilder
            = MockMvcBuilders.standaloneSetup(optInController, actieLogController);
    RestAssuredMockMvc.standaloneSetup(standaloneMockMvcBuilder);

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.registerModule(new JavaTimeModule());

    OptIn createOptIn = objectMapper.readValue(new File("src/test/resources/contracts/vennotenDossiersConsumer/registerOptInRequestBody.json"), OptIn.class);
    OptIn getOptIn = objectMapper.readValue(new File("src/test/resources/contracts/vennotenDossiersConsumer/getOptInResponseBody.json"), OptIn.class);
    List<OptIn> getOptInByLiantisId = objectMapper.readValue(new File("src/test/resources/contracts/vennotenDossiersConsumer/getOptInByLiantisIdResponseBody.json"), new TypeReference<List<OptIn>>() {});
    List<OptIn> getAllOptIns = objectMapper.readValue(new File("src/test/resources/contracts/vennotenDossiersConsumer/getAllOptInsResponseBody.json"), new TypeReference<List<OptIn>>() {});
    List<ActieLog> getAllActionLogs = objectMapper.readValue(new File("src/test/resources/contracts/vennotenDossiersConsumer/getAllActionLogsResponseBody.json"), new TypeReference<List<ActieLog>>() {});
    List<ActieLog> getActionLogsByLiantisId = objectMapper.readValue(new File("src/test/resources/contracts/vennotenDossiersConsumer/getActionLogsByLiantisIdResponseBody.json"), new TypeReference<List<ActieLog>>() {});
    List<ActieLog> getActionLogsByGebruikerId = objectMapper.readValue(new File("src/test/resources/contracts/vennotenDossiersConsumer/getActionLogsByGebruikerIdResponseBody.json"), new TypeReference<List<ActieLog>>() {});

    when(optInService.createOptIn(any(OptIn.class))).thenReturn(createOptIn);
    when(optInService.getOptIn(liantisId, applicatieNaam, optInGebruikerId)).thenReturn(getOptIn);
    when(optInService.getAllOptIns()).thenReturn(getAllOptIns);
    when(optInService.getOptInsByLiantisId(liantisId)).thenReturn(getOptInByLiantisId);
    when(actieLogService.getAllActionLogs()).thenReturn(getAllActionLogs);
    when(actieLogService.getActionLogsByLiantisId(liantisId)).thenReturn(getActionLogsByLiantisId);
    when(actieLogService.getActionLogsByGebruikerId(gebruikerId)).thenReturn(getActionLogsByGebruikerId);
}
}

groovy script:

Contract.make {
description "should return an Action Log for GebruikerId"
request{
    url("api/actionlogs") {
        headers {
            contentType(applicationJson())
        }
        queryParameters {
            parameter("gebruikerId","gebruikerId")
        }
    method GET()
    }
}
response {
    status OK()
    headers {
        contentType(applicationJson())
    }
    body(file("getActionLogsByGebruikerIdResponseBody.json"))
}
}

ResponseBody.json:

[
{
"liantisId": "liantisId1",
"entiteitActie": "prospect",
"applicatieNaam": "applicatieNaam1",
"actie": "CREATE",
"optInGebruiker": {
  "gebruikerId": "gebruikerId",
  "username": "username1",
  "qualificatie": "qualificatie1"
},
"actionLoggedDate": "2020-03-02T09:54:10.758"
}
]

generated Test:

public class VennotenDossiersConsumerTest extends VennotenDossiersConsumerBase {

@Test
public void validate_getActionLogsByGebruikerId() throws Exception {
    // given:
        MockMvcRequestSpecification request = given()
                .header("Content-Type", "application/json");

    // when:
        ResponseOptions response = given().spec(request)
                .queryParam("gebruikerId","gebruikerId")
                .get("api/actionlogs");

    // then:
        assertThat(response.statusCode()).isEqualTo(200);
        assertThat(response.header("Content-Type")).matches("application/json.*");

    // and:
        DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
        assertThatJson(parsedJson).array().contains("['liantisId']").isEqualTo("liantisId1");
        assertThatJson(parsedJson).array().contains("['entiteitActie']").isEqualTo("prospect");
        assertThatJson(parsedJson).array().contains("['applicatieNaam']").isEqualTo("applicatieNaam1");
        assertThatJson(parsedJson).array().contains("['actie']").isEqualTo("CREATE");
        assertThatJson(parsedJson).array().field("['optInGebruiker']").field("['gebruikerId']").isEqualTo("gebruikerId");
        assertThatJson(parsedJson).array().field("['optInGebruiker']").field("['username']").isEqualTo("username1");
        assertThatJson(parsedJson).array().field("['optInGebruiker']").field("['qualificatie']").isEqualTo("qualificatie1");
        assertThatJson(parsedJson).array().contains("['actionLoggedDate']").isEqualTo("2020-03-02T09:54:10.758");
}

error i get when running the test:

java.lang.IllegalStateException: Parsed JSON [[{"liantisId":"liantisId","entiteitActie":"prospect","applicatieNaam":"applicatieNaam1","actie":"CREATE","optInGebruiker":{"gebruikerId":"gebruikerId1","username":"username1","qualificatie":"qualificatie1"},"actionLoggedDate":[2020,3,2,9,54,10,758000000]}]] doesn't match the JSON path [$[*][?(@.['actionLoggedDate'] == '2020-03-02T09:54:10.758')]]

    at com.toomuchcoding.jsonassert.JsonAsserter.check(JsonAsserter.java:228)
    at com.toomuchcoding.jsonassert.JsonAsserter.checkBufferedJsonPathString(JsonAsserter.java:267)
    at com.toomuchcoding.jsonassert.JsonAsserter.isEqualTo(JsonAsserter.java:101)
    at be.liantis.gdpr.service.contracts.VennotenDossiersConsumerTest.validate_getActionLogsByLiantisId(VennotenDossiersConsumerTest.java:70)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:532)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:171)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:167)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:114)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:59)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:108)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
    at java.util.ArrayList.forEach(ArrayList.java:1257)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
    at java.util.ArrayList.forEach(ArrayList.java:1257)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)

So when the get request is executed in the generated test, it returns the localDateTime as an array. I've been trying to assign custom ObjectMappers to the RestAssuredMockMVC but nothing is working.

if i use

RestAssuredMockMvc.config().objectMapperConfig(ObjectMapperConfig.objectMapperConfig().jackson2ObjectMapperFactory((type, s) -> OBJECT_MAPPER));

it doesn't work.

if i pass it to the standaloneSetup() as a parameter is doesn't work either.

I've tried the mockMVC message converter too, doesn't work either.

Has anyone had this problem before? I can't find any examples for Spring contract that use LOCALDATE or LOCALDATETIME either.

The Tests that don't contain a localDateTime succeed without any problems.

Anyone know what to do?


Solution

  • I solved the problem myself, I've stopped using the standalone setup.

    I gave RestAssured my webappcontext and then it used the correct objectmapper.

    RestAssuredMockMvc.webAppContextSetup(appContext)
    

    where the appContext was my Autowired WebApplicationContext:

    @Autowired
    private WebApplicationContext appContext;
    

    To bypass the 401 I was getting because of the security context I used @WithMockUser on the base class.

    @WithMockUser
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
    public abstract class VennotenDossiersConsumerBase