0

在我的 Citrus 测试中,我试图验证嵌入在 JSON 文档(WireMock 的输出)中的 XML 消息。XML 文本需要解析,因为它包含我想忽略的时间戳。JSON 消息的 XML 部分如下所示:

"requests": [
{
    "id": "52844d5a-59de-4288-8000-7f48fcda41e5",
    "request": {
        "body": "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"> [content omitted] </soapenv:Body></soapenv:Envelope>",
}
]

这是我的 Java 测试代码:

http()
    .client(wiremockClient)
    .receive()
    .response(HttpStatus.OK)
    .messageType(MessageType.JSON)
    .extractFromPayload("$.requests[0].request.body", "body")
    .payload(new ClassPathResource("output/esb/add_conf_to_cart/response2.xml"))
;

其中 respon2.xml 仅包含 XML 文本(上面显示的 JSON 消息中“body”的内容),我从 Citrus 得到的错误是

com.consol.citrus.exceptions.CitrusRuntimeException: Failed to parse JSON text
…
Caused by: net.minidev.json.parser.ParseException: Unexpected token <soapenv:Envelope xmlns:soapenv=

那么,如何验证嵌入在 JSON 文档中的 XML?

4

3 回答 3

2

使用 gucci 的解决方案,我终于找到了验证 XML 的正确语法。代码现在如下所示:

        variable("jsonXml", "'citrus:readFile('classpath:output/esb/add_conf_to_cart/response2.xml')'");

        http()
                .client(wiremockClient)
                .send()
                .get("/__admin/requests")
                .accept("application/json");

        HttpClientResponseActionBuilder body = http()
                .client(wiremockClient)
                .receive()
                .response(HttpStatus.OK)
                .messageType(MessageType.JSON)
                .validate("$.requests[0].request.body", "@matchesXml(${jsonXml})@")
                ;

我犯的错误首先是忘记了第二个@,然后在参数周围添加了单引号以匹配XML。所以

.validate("$.requests[0].request.body", "@matchesXml('${jsonXml}')@")

不会工作,但

.validate("$.requests[0].request.body", "@matchesXml(${jsonXml})@")

将完成这项工作。

于 2017-10-20T18:12:04.523 回答
1

澄清

我对您当前的解决方案有两点意见:

  1. extractFromPayload(..)body将JSON 的一部分提取到一个名为的 Citrus 变量body中。这与零件没有任何关系payload(..)。您刚刚将 JSON 内容存储body 到 Citrus 变量body中。您可能知道也可能不知道这一点

  2. payload(..)方法将始终验证接收到的整个有效负载,即整个 JSON 对象。

  3. 如果您只想验证响应的一部分,请使用JSON 路径验证.validate("$.some.json.path", "someValue")

解决方案

Citrus 为您的用例提供了一个内部方法,请参阅matchesXml()的文档。

仅验证 XML 结构

这是最简单的解决方案:

http()
    .client(wiremockClient)
    .receive()
    .response(HttpStatus.OK)
    .messageType(MessageType.JSON)
    .validate("$.requests[0].request.body", @matchesXml('citrus:readFile('classpath:output/esb/add_conf_to_cart/response2.xml')')@)

验证整个 JSON 响应

在您的情况下,您只需匹配整个 JSON 结构并将方法@matchesXml('<some><validation_xml></some></validation_xml>')@放入正确的 JSON 条目中:

http()
.client(wiremockClient)
.receive()
.response(HttpStatus.OK)
.messageType(MessageType.JSON)
.payload("{\n"+
    "  \"requests\": {\n"+
    "    \"id\": \"52844d5a-59de-4288-8000-7f48fcda41e5\",\n"+
    "    \"request\": {\n"+
    "      \"body\": \"@matchesXml('<soapenv:Envelope xmlns:soapenv=\\\"http://schemas.xmlsoap.org/soap/envelope/\\\"> [content omitted] </soapenv:Body></soapenv:Envelope>')@\"\n"+
    "    }\n"+
    "  }\n"+
    "}");

评论

  • 放入的字符串payload必须是有效的 JSON,即 JSON 值中的引号必须被转义。
  • Java字符串中的引号也必须被转义,因此我们必须写\\\"
  • Citrus 首先将字符串解析payload为 JSON,并在内部将其保留为 JSON 对象,其中转义引号未转义。然后它将这个字符串传递给验证函数matchesXml
  • 您当前的示例不是有效的 XML,因为标签</soapenv:Body>从未打开
  • 将有效负载请求放入单独的文件中要容易得多,只需注意文件仍然必须是有效的 JSON,即用一个反斜杠转义的引号,如下所示\"
  • 您可能希望从单独的文件中读取 XML 内容。在这种情况下,您可以使用 Citrus 的citrus:readFile()功能
  • 对于payload,您仍然需要在 XML 中转义引号。你可以这样做citrus:translate()citrus:translate('citrus:readFile('classpath:some/path/to/response.xml')', '\"', '\\"')
  • 转义非常棘手,因为我们正在处理 Java、JSON 和 XML 中特殊的引号和反斜杠
  • 它应该按如下方式工作:

创建一个文件response_validation.json

{
  "requests": {
    "id": "52844d5a-59de-4288-8000-7f48fcda41e5",
    "request": {
      "body": "@matchesXml('${jsonEscapedXmlInput}')@"
    }
  }
}

在您的测试用例中,创建jsonEscapedXmlInput读取和转义 XML 文件的 Citrus 变量:

variable("jsonEscapedXml", "citrus:translate('citrus:readFile('classpath:output/esb/add_conf_to_cart/response2.xml')', '\\\"', '\\\\\"')")

然后使用它

http()
    .client(wiremockClient)
    .receive()
    .response(HttpStatus.OK)
    .messageType(MessageType.JSON)
    .payload(new ClassPathResource("classpath:validation/response_validation.json"))
;
于 2017-10-20T10:12:40.453 回答
0

另一个解决方案使用 AbstractTestAction 的实现来调用在 Citrus 上下文中进行验证的方法:

    http()
            .client(wiremockClient)
            .receive()
            .response(HttpStatus.OK)
            .messageType(MessageType.JSON)
            .extractFromPayload("$.requests[0].request.body", "body")
    ;
    action(action(new DummyAction(new File(EXPECTED_OUTPUT_FILE)));

action() 调用注册 DummyAction 以执行验证。在我的情况下,它需要一个文件来验证通过 http().extractFromPayload("$.requests[0].request.body", "body") 从有效负载中提取的正文内容

DummyAction 类如下所示:

class DummyAction extends AbstractTestAction {

    private File expectedOutputFile;

    public DummyAction(File expectedOutputFile) {
        this.expectedOutputFile = expectedOutputFile;
    }

    @Override
    public void doExecute(TestContext testContext) {
        String body = testContext.getVariable("body");
        Assert.assertTrue(body.startsWith("<soapenv:Envelope "), "Request should start with '<soapenv:Envelope '");
    }
}

doExecute() 可以访问测试上下文中可用的变量。因此 extractFromPayload() 在上下文中放置了一个名为“body”的变量,然后在 doExecute() 中访问该变量。该解决方案允许运行任意 Java 代码,在我的例子中是从 SOAP 信封中提取 XML 文档,并隐藏在 JSON 文档中。

于 2017-11-07T19:13:02.503 回答