When writing integration tests, have you ever needed to match a JSON object against a specific pattern? I did, recently. The solution I found was an elegant hack (isn’t that an oximoron?) using the excellent MockServer. Let's explore how this hack works and why I chose this solution.
I was writing an integration test for one of the Walletera services, the payments service. What I wanted to verify in the test was this:
When a payment is created
Then the payments service publishes the following event: ...
In Walletera, the events are JSON objects, so I needed a way to verify that a JSON object conforms with a certain structure. This wasn't an exact comparison because some parts are dynamic, like current timestamps, JWT tokens, UUIDs, etc. So, I needed a JSON matcher. Naturally, my first step was to search for "golang json matcher" online. After reviewing the results, the most promising Go package that I found was https://github.com/panta/go-json-matcher. I decided to try it out. It works, but there's a drawback. It only tells you if the JSON matched the specification or not, without providing any additional information about why the JSON didn't match. For simple JSON objects, this solution might be sufficient, but for more complex cases, it falls short.
So, I need a JSON matcher that allows me to match JSON objects with dynamic elements and also, in the case of a mismatch, tells me where the difference is between the actual JSON object and the expected JSON structure.
I was already using in my integration tests a component that satisfied almost all my needs, the great MockServer. It has an excellent JSON matcher, and in the case of a mismatch, you can check the logs to see exactly why the match failed. The only problem was that MockServer verifies HTTP requests, not random JSON objects. After thinking a bit I said, fuck it, let’s embeded our event JSON into an HTTP request. It is slower than using a matching library, but in the context of an integration test, this is negligible.
Now, let’s see how we can build a generic JSON matcher using MockServer. It’s very simple, we just need to create a generic MockServer expectation. The HTTP method and path could be whatever you like. I choose POST as the method and /matchevent as the path.
httpRequestExpectationWrapperTemplate := `
{
"id": "%s",
"httpRequest" : {
"method": "POST",
"path": "/matchevent",
"body": {
"type": "JSON",
"json": %s,
"matchType": "ONLY_MATCHING_FIELDS"
}
},
"httpResponse" : {
"statusCode" : 201,
"headers" : {
"content-type" : [ "application/json" ]
}
},
"priority" : 0,
"timeToLive" : {
"unlimited" : true
},
"times" : {
"unlimited" : true
}
}
`
Note the placeholders %s in the string. This string is a template; we can instantiate it with an expectation id and a JSON matcher like this:
httpRequestExpectationWrapper := fmt.Sprintf(httpRequestExpectationWrapperTemplate, expectationId, eventMatcher)
The expectationId will be used later to ask MockServer if the expectation was or was not met. The eventMatcher is a JSON object with optional placeholders to match objects with dynamic fields. Let's see an example matcher.
{
"id": "${json-unit.regex}^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$",
"type": "PaymentCreated",
"data": {
"id": "${json-unit.regex}^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$",
"amount": 100,
"currency": "ARS",
"direction": "inbound",
"customerId": "${json-unit.regex}^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$",
"status": "pending",
"beneficiary": {
"bankName": "dinopay",
"bankId": "dinopay",
"accountHolder": "John Doe",
"routingKey": "123456789123456"
},
"createdAt": "${json-unit.any-string}"
}
}
For more info on the supported options for the JSON matcher, check the MockServer documentation.
Now, the best part is that if the match fails, you check the MockServer logs, which looks like this

The output tells you the exact field (or fields) that didn’t match (in this case data.direction), the expected value, and the provided value.
And that’s it, we built a generic and powerful JSON matcher using MockServer. I hope you find this hack useful. See you at the next stop of the Walletera journey.