Wiremock: Support Json-Unit placeholders in equalToJson

Created on 25 Apr 2019  路  5Comments  路  Source: tomakehurst/wiremock

As mentioned in This Topic,I would like to include JsonUnit with contribution from : Anas Aitbaha in order to ignore value when request matching using equalToJson as it is the case in equalToXml body pattern .

example from json-unit :
assertThatJson("{\"test\":1}").isEqualTo("{\"test\":\"${json-unit.ignore}\"}");

Most helpful comment

Possibly a more elegant solution for verification could be:

verify(requestMadeFor(value -> {
    assertThatJson(value.getBodyAsString()).withMatcher("matcherName", new SomeCustomMatcher())
        .isEqualTo(expectedBody);
    return MatchResult.exactMatch();
}));

Now, you've left JSON assertion completely to json-unit. Note that the custom matcher is not required; json-unit.ignore will work out of the box, but custom matches are useful.

All 5 comments

Thanks for raising this. I think using JsonUnit might be a great idea.

Here are my initial thoughts (which I'm sure I'll end up adding to once I've had more time to think about it):

  • This should probably replace the current implementation of EqualToJsonPattern. It should therefore maintain backwards compatibility, including the existing ignoreExtraElements and ignoreArrayOrder parameters.
  • WireMock calculates a distance value between a stub and a request when they're not an exact match. All matcher implementations must support this kind of distance calculation therefore, so we'll need a way to do this. EqualToXmlPattern achieves this by attaching a ComparisonListener to XMLUnit and incrementing each time a node doesn't match.
  • We need to take some care over performance, as this is likely to get used very heavily in some contexts. Ideally we should load test/micro-benchmark.

Here is a quick fix that I ve chosen to get some results. The quality of the code is not good , but it still works well.

```@Override
public MatchResult match(String value) {
try {
final JsonNode actual = Json.read(value, JsonNode.class);
final String actualJson = value;
return new MatchResult() {
@Override
public boolean isExactMatch() {
// Try to do it the fast way first, then fall back to doing the full diff
if (!shouldIgnoreArrayOrder() && !shouldIgnoreExtraElements()) {
try {
assertThatJson(expected).isEqualTo(actual);
return true;
} catch (Error e) {
return getDistanceWhenIsNotExactMatch()==0;
}
} else if (shouldIgnoreArrayOrder() && shouldIgnoreExtraElements()) {
try {
assertThatJson(expectedJson).when(Option.IGNORING_ARRAY_ORDER).when(IGNORING_EXTRA_FIELDS).isEqualTo(actualJson);
return true;
} catch (Error e) {
try {
assertThatJson(actualJson).when(Option.IGNORING_ARRAY_ORDER).when(IGNORING_EXTRA_FIELDS).isEqualTo(expectedJson);
return true;
} catch (Error aE) {
return getDistanceWhenIsNotExactMatch()==0;
}
}
} else if (!shouldIgnoreArrayOrder() && shouldIgnoreExtraElements()) {

                    try {
                        assertThatJson(actualJson).when(IGNORING_EXTRA_FIELDS).isEqualTo(expectedJson);
                        return true;
                    } catch (Error e) {
                        try {
                            assertThatJson(expectedJson).when(IGNORING_EXTRA_FIELDS).isEqualTo(actualJson);
                            return true;
                        } catch (Error aE) {
                            return getDistanceWhenIsNotExactMatch()==0;
                        }
                    }
                } else if (shouldIgnoreArrayOrder() && !shouldIgnoreExtraElements()) {
                    try {
                        assertThatJson(expectedJson).when(Option.IGNORING_ARRAY_ORDER).isEqualTo(actual);
                        return true;
                    } catch (Error e) {
                        return getDistanceWhenIsNotExactMatch()==0;
                    }
                }

                return false;
            }

            @Override
            public double getDistance() {
                if (!isExactMatch()) {

                    return getDistanceWhenIsNotExactMatch();
                } else {
                    return 0.0;
                }
            }
            public double getDistanceWhenIsNotExactMatch() {
                EnumSet<DiffFlags> flags = EnumSet.of(OMIT_COPY_OPERATION);
                ArrayNode diff = (ArrayNode) JsonDiff.asJson(expected, actual, flags);

                double maxNodes = maxDeepSize(expected, actual);

                    return diffSize(diff) / maxNodes;
            }
        };
    } catch (Exception e) {
        return MatchResult.noMatch();
    }
}

```

I'm not keen on the idea of using exceptions for control flow as there's quite a big performance penalty doing it that way.

Presumably it's possible to call the API a layer down so that you can get a boolean or diff result of some kind rather than make an assertion?

Possibly a more elegant solution for verification could be:

verify(requestMadeFor(value -> {
    assertThatJson(value.getBodyAsString()).withMatcher("matcherName", new SomeCustomMatcher())
        .isEqualTo(expectedBody);
    return MatchResult.exactMatch();
}));

Now, you've left JSON assertion completely to json-unit. Note that the custom matcher is not required; json-unit.ignore will work out of the box, but custom matches are useful.

This would be a great addition for much more expressive pattern matching of JSON body content. Unfortunately it doesn鈥檛 look like this can be changed externally via an extension and must replace the current EqualToJsonPattern. However, I think it will definitely improve both common and advanced use cases.

Was this page helpful?
0 / 5 - 0 ratings