Skip to content

Instantly share code, notes, and snippets.

@bethesque
Last active January 1, 2016 04:29
Show Gist options
  • Save bethesque/5a35a3c1cb9fdab6dce7 to your computer and use it in GitHub Desktop.
Save bethesque/5a35a3c1cb9fdab6dce7 to your computer and use it in GitHub Desktop.
An idea of what a flexible matching fluent API might look like, and how it could be serialised.
# Notes:
# Headers and status would still be value based matching
# Default to type based matching???
# like(xxx) means xxx and all children would be matched using type based matching
# literal(xxx) means xxx and all children would be matched using exact value matching
# eg(generate, matcher) would be a regular expression match
# each_like([]) would indicate the each element in the array should be like the example given
# A like() could be nested inside a literal() which could be nested inside a like() - at each stage,
# it changes the matching type for all children until it gets switched back.
# Outstanding questions:
# Should array order matter?
# How to specify something like "an array of strings, length unknown" (eg. the backtrace of an exception)
# Ruby code
will_respond_with(
{
status: 200,
headers: {
'Content-Type': 'application/json'
},
body: {
age: 30,
address: literal({
street: '123 Fred St',
suburb: like('Melbourne'),
postcode: '3000',
state: eg('VIC',/[A-Z]{3}/)
}),
phoneNumber: eg("0415 134 234", /\d{4} \d{3} \d{3}/),
favouriteColors: each_like(['red']),
an_empty_array: [],
url: eg('http://localhost:1234/blah', %r{http://.*/blah})
}
})
# Generated JSON
{
"responseMatchingRules": {
"body" : {
"$..*": {"match": "type"},
"$.address": {"match":"value", "allowExtraKeys": false},
"$.address.suburb": {"match":"type"},
"$.phoneNumber": {"regex" : "\d{4} \d{3} \d{3}"},
"$.favouriteColors": {"eachLike": true },
"$.url": {"regex" : "http://.*/blah"}
}
},
"response": {
"body": {
"age": 30,
"address": {
"street": "123 Fred St",
"suburb": "Melbourne",
"postcode": "3000",
"state": "VIC"
},
"phoneNumber": "0415 134 234",
"favouriteColors": [
"red"
],
"an_empty_array": [],
"parents": [
"mum",
"dad"
],
"url": "http: //localhost:1234/blah"
}
}
}
end
@bethesque
Copy link
Author

I get the impression that this use case would be most useful when using pact to back an integration test on the consumer (I assume that's why you don't know what the event ID is ahead of time), which I don't think is it's best use, because we still get the combinatorial problem. That's why the Condor tests were the worst example of pact usage that we had! I will put it in an issue as a feature that we need to consider for 2.0.0 though.

@rpocklin
Copy link

I've done a bit of work on JSON-based matchers before.

For specifying types (ie. Array / Hash)
what about:

friends: type(Array)
age: type(String)

but it almost feels like you will need a Hash declaration for each property to define a set of rules/matchers:

  • type
  • length
  • regex / pattern(s)
  • custom validations (eg. call a method)

@bethesque
Copy link
Author

There are a couple of reasons I've stuck with the "here's an example" technique rather than the "define the types" technique. One is that pact would have to randomly generate values for the response in the consumer project, and the request when verifying a pact, and randomly generated values are not great for the auto generated documentation. The second is that, as you say, you'd have to start specifying the lengths as well, or you'd get random errors if the generated string was too long. This would add complexity to both the definition and the code. I'm not discounting the idea entirely, but it's not the direction I've been heading in, after giving both concepts a good deal of thought.

@thetrav
Copy link

thetrav commented Feb 1, 2015

Super late to the party on this one.

The good thing about pact 1.0 is that you have a clear document indicating what's being tested structurally.

With the jsonpath matching rules, you've still got your structure, but then you've got a list of rules which are exceptions to the structure. Building a mental model of where those rules apply is challenging, it harms readability, it's less a document and more of a serialised program.

Unfortunately I don't have a good solution, documents are naturally less flexible than programs, and programs are naturally less readable than documents.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment