Skip to content

Add matcher "hasJsonBody"#265

Merged
AliSoftware merged 4 commits intoAliSoftware:masterfrom
pimnijman:feature/has-json-body
Oct 23, 2017
Merged

Add matcher "hasJsonBody"#265
AliSoftware merged 4 commits intoAliSoftware:masterfrom
pimnijman:feature/has-json-body

Conversation

@pimnijman
Copy link
Contributor

Checklist

  • I've checked that all new and existing tests pass
  • I've updated the documentation if necessary
  • I've added an entry in the CHANGELOG to credit myself

Description

I've added a matcher called hasJsonBody that tests whether the NSURLRequest body 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 hasBody isn'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.swift that test a handful of scenarios. Please check them out and let me know if you think I overlooked some important scenarios

Copy link
Owner

@AliSoftware AliSoftware left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 {
Copy link
Owner

@AliSoftware AliSoftware Oct 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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]]),
]
Copy link
Owner

@AliSoftware AliSoftware Oct 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
    }

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, exactly 👍

func testHasJsonBodyIsFalse() {
let jsonObjects = [
// Changed value
(["foo": "bar"],
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same idea here of course

…ionaries to make sure the order won't be messed up before creating the data
@pimnijman
Copy link
Contributor Author

@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 = [
Copy link
Owner

@AliSoftware AliSoftware Oct 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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" ] ]

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I didn't realize you used some test input strings multiple times, good point

@AliSoftware
Copy link
Owner

@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

@AliSoftware AliSoftware merged commit aa36259 into AliSoftware:master Oct 23, 2017
@417-72KI 417-72KI mentioned this pull request Dec 4, 2019
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants