Spring Cloud Contract LocalDateTime 断言失败

Spring Cloud Contract LocalDateTime assertions fail

最后一天,我一直在努力处理 Spring 合同测试中的 LocalDateTime。

基础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 脚本:

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"
}
]

生成的测试:

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");
}

我在 运行 测试时得到的错误:

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(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(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(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(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(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)

因此在生成的测试中执行get请求时,它returns localDateTime作为一个数组。 我一直在尝试将自定义 ObjectMappers 分配给 RestAssuredMockMVC,但没有任何效果。

如果我使用

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

没用。

如果我将它作为参数传递给 standaloneSetup() 也不起作用。

我也试过 mockMVC 消息转换器,也没用。

以前有人遇到过这个问题吗? 我找不到任何使用 LOCALDATE 或 LOCALDATETIME 的 Spring 合同示例。

不包含 localDateTime 的测试成功,没有任何问题。

有人知道该怎么做吗?

我自己解决了这个问题,我已经停止使用独立设置。

我为 RestAssured 提供了我的 webappcontext,然后它使用了正确的对象映射器。

RestAssuredMockMvc.webAppContextSetup(appContext)

appContext 是我的 Autowired WebApplicationContext:

@Autowired
private WebApplicationContext appContext;

为了绕过我因为安全上下文而得到的 401,我在基础上使用了 @WithMockUser class。

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