Add matcher "hasJsonBody"#265
Conversation
AliSoftware
left a comment
There was a problem hiding this comment.
Thanks a lot for the PR, that's a great idea!
Just a couple of remarks to make that more robust on unit tests 👍
| #if swift(>=3.0) | ||
| public func hasJsonBody(_ jsonObject: [AnyHashable : Any]) -> OHHTTPStubsTestBlock { | ||
| return { req in | ||
| guard let jsonBody = (try? JSONSerialization.jsonObject(with: (req as NSURLRequest).ohhttpStubs_HTTPBody(), options: [])) as? [AnyHashable : Any] else { |
There was a problem hiding this comment.
We very recently fixed that need for the cast to NSURLRequest (see #260) so as can be removed to simplify this expression :)
| // Nested objects with changed attribute order | ||
| (["foo": "bar", "baz": ["qux": true, "quux": ["spam", "ham", "eggs"]]], | ||
| ["foo": "bar", "baz": ["quux": ["spam", "ham", "eggs"], "qux": true]]), | ||
| ] |
There was a problem hiding this comment.
The problem with that approach is that dictionaries have no guaranteed order (and that's the whole point of your PR). So there's no guarantee that Swift won't make all those dictionaries exactly the same internally, and then making the JSON serialized version of all those exactly the same (e.g. one might even imagine that JSONSerialization.data(withJSONObject:) is implemented so that it iterates over the dictionaries' keys in alphabetical order… who knows…)
To solve that, I think you'll instead need to use the already-converted-to-JSON String representations as a test set instead.
Which would have the additional benefit of proving that your matcher is also robust against different indentations of JSON bodies.
One way to do that is to use string literals of JSON representations to test
let jsonObjects = [
"{ \"foo\": \"bar\", \n\"baz\": 42, \"qux\": true }",
…
But for readability of the unit tests, maybe outsourcing those texts/JSON representation in fixture files would be nicer.
There was a problem hiding this comment.
Your suggestion makes sense, but just to be clear; what you're suggesting is the following:
let jsonStrings = [
("{ \"foo\": \"bar\", \"baz\": 42, \"qux\": true }",
["foo": "bar", "baz": 42, "qux": true])
// Etc...
]
for (jsonString, expectedJsonObject) in jsonStrings {
var req = URLRequest(url: URL(string: "foo://bar")!)
req.httpBody = jsonString.data(using: .utf8)
let matchesJsonBody = hasJsonBody(expectedJsonObject)(req)
XCTAssertTrue(matchesJsonBody)
}
| func testHasJsonBodyIsFalse() { | ||
| let jsonObjects = [ | ||
| // Changed value | ||
| (["foo": "bar"], |
… is no longer needed
…ionaries to make sure the order won't be messed up before creating the data
|
@AliSoftware, should I create a new pull request or is there a way to resubmit this one? I'm new to this. :) |
|
|
||
| #if swift(>=3.0) | ||
| func testHasJsonBodyIsTrue() { | ||
| let jsonStringsAndObjects = [ |
There was a problem hiding this comment.
Nitpicking, but just realized: why make that an array of tuples, instead of a Dictionary?
Wouldn't change a thing in your for loop below, but would be more common Swift and would make it order-independent.
There was a problem hiding this comment.
I'm not sure I agree. Of course I could convert this array to a dictionary but this would mean I can use each JSON sting only once. Unless I've misinterpreted your comment.
let dict = [ "{ \"foo\": \"bar\" }", ["foo": "bar" ] ]
There was a problem hiding this comment.
Oh I didn't realize you used some test input strings multiple times, good point
|
@pimnijman No need to create a new PR 😃 A pull request is a request to merge a branch into another branch, so if you push new commits to this branch like you just did, even after creating the initial PR, they'll be included in the PR as well, no need to create a new one 😃 I'll merge as soon as the CI (Travis) is ready |
Checklist
Description
I've added a matcher called
hasJsonBodythat tests whether theNSURLRequestbody contains a JSON object with the same keys and values.Motivation and Context
Since it shouldn't matter in what way the attributes of a JSON object are ordered, trying to match the URL request's body using
hasBodyisn't very handy when it comes to verifying JSON content. That is why I've created a matcher that takes a JSON object and matches is with whatever JSON object it can find in the URL request's body.I've added two methods in
SwiftHelpersTests.swiftthat test a handful of scenarios. Please check them out and let me know if you think I overlooked some important scenarios