From 736e9294be55a6c08d4c8b0fad3071beb5ca1204 Mon Sep 17 00:00:00 2001 From: Pim Nijman Date: Fri, 20 Oct 2017 14:21:06 +0200 Subject: [PATCH 1/4] Add matcher "hasJsonBody" --- .../Sources/Swift/OHHTTPStubsSwift.swift | 17 +++++ .../Test Suites/SwiftHelpersTests.swift | 66 +++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift b/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift index e32df03c..0cee53b0 100644 --- a/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift +++ b/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift @@ -392,6 +392,23 @@ public func hasHeaderNamed(_ name: String, value: String) -> OHHTTPStubsTestBloc } #endif +/** + * Matcher testing that the `NSURLRequest` body contains a JSON object with the same keys and values + * - Parameter jsonObject: the JSON object to expect + * + * - Returns: a matcher that returns true if the `NSURLRequest`'s body contains a JSON object with the same keys and values as the parameter value + */ +#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 { + return false + } + return NSDictionary(dictionary: jsonBody).isEqual(to: jsonObject) + } +} +#endif + // MARK: Operators on OHHTTPStubsTestBlock /** diff --git a/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift b/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift index 3c366db4..286fc429 100644 --- a/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift +++ b/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift @@ -454,6 +454,72 @@ class SwiftHelpersTests : XCTestCase { #endif } +#if swift(>=3.0) + func testHasJsonBodyIsTrue() { + let jsonObjects = [ + // Exact match + (["foo": "bar", "baz": 42, "qux": true], + ["foo": "bar", "baz": 42, "qux": true]), + // Changed attribute order + (["foo": "bar", "baz": 42, "qux": true], + ["qux": true, "foo": "bar", "baz": 42]), + // Nested objects + (["foo": "bar", "baz": ["qux": true, "quux": ["spam", "ham", "eggs"]]], + ["foo": "bar", "baz": ["qux": true, "quux": ["spam", "ham", "eggs"]]]), + // Nested objects with changed attribute order + (["foo": "bar", "baz": ["qux": true, "quux": ["spam", "ham", "eggs"]]], + ["foo": "bar", "baz": ["quux": ["spam", "ham", "eggs"], "qux": true]]), + ] + + for (jsonObject, expectedJsonObject) in jsonObjects { + var req = URLRequest(url: URL(string: "foo://bar")!) + + let data = try! JSONSerialization.data(withJSONObject: jsonObject, options: []) + req.httpBody = data + + let matchesJsonBody = hasJsonBody(expectedJsonObject)(req) + + XCTAssertTrue(matchesJsonBody) + } + } +#endif + +#if swift(>=3.0) + func testHasJsonBodyIsFalse() { + let jsonObjects = [ + // Changed value + (["foo": "bar"], + ["foo": "qux"]), + // Changed key + (["foo": "bar"], + ["baz": "bar"]), + // Missing attribute + (["foo": "bar", "baz": 42], + ["baz": "bar"]), + // Extraneous attribute + (["foo": "bar"], + ["foo": "bar", "baz": 42]), + // Changed order in array + (["foo": ["spam", "ham", "eggs"]], + ["foo": ["spam", "eggs", "ham"]]), + // Nested objects with changed value + (["foo": "bar", "baz": ["qux": true]], + ["foo": "bar", "baz": ["qux": false]]) + ] + + for (jsonObject, expectedJsonObject) in jsonObjects { + var req = URLRequest(url: URL(string: "foo://bar")!) + + let data = try! JSONSerialization.data(withJSONObject: jsonObject, options: []) + req.httpBody = data + + let matchesJsonBody = hasJsonBody(expectedJsonObject)(req) + + XCTAssertFalse(matchesJsonBody) + } + } +#endif + let sampleURLs = [ // Absolute URLs "scheme:", From d0af2325fc02524ded61739ec386569500719a72 Mon Sep 17 00:00:00 2001 From: Pim Nijman Date: Fri, 20 Oct 2017 14:43:41 +0200 Subject: [PATCH 2/4] Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a95a63de..f83f3209 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Master +* Added `hasJsonBody(_:)` matcher. + [@pimnijman](https://github.com/pimnijman) * Added `onStubMissing` to report missing stubs. [@c1ira](https://github.com/c1ira) [#264](https://github.com/AliSoftware/OHHTTPStubs/pull/264) From 7728a0b1b35e280f8797db78a981efb0511b708d Mon Sep 17 00:00:00 2001 From: Pim Nijman Date: Fri, 20 Oct 2017 16:41:43 +0200 Subject: [PATCH 3/4] Use URLRequest's ohhttpStubs_httpBody so that casting to NSURLRequest is no longer needed --- OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift b/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift index 0cee53b0..963ac88a 100644 --- a/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift +++ b/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift @@ -401,7 +401,10 @@ public func hasHeaderNamed(_ name: String, value: String) -> OHHTTPStubsTestBloc #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 { + guard + let httpBody = req.ohhttpStubs_httpBody, + let jsonBody = (try? JSONSerialization.jsonObject(with: httpBody, options: [])) as? [AnyHashable : Any] + else { return false } return NSDictionary(dictionary: jsonBody).isEqual(to: jsonObject) From a053ca5010fcb7232e129f1b8d853ebeabd807d4 Mon Sep 17 00:00:00 2001 From: Pim Nijman Date: Mon, 23 Oct 2017 11:05:26 +0200 Subject: [PATCH 4/4] Use string literals to create the request's HTTP body instead of dictionaries to make sure the order won't be messed up before creating the data --- .../Test Suites/SwiftHelpersTests.swift | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift b/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift index 286fc429..3b636466 100644 --- a/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift +++ b/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift @@ -456,27 +456,27 @@ class SwiftHelpersTests : XCTestCase { #if swift(>=3.0) func testHasJsonBodyIsTrue() { - let jsonObjects = [ + let jsonStringsAndObjects = [ // Exact match - (["foo": "bar", "baz": 42, "qux": true], + ("{ \"foo\": \"bar\", \"baz\": 42, \"qux\": true }", ["foo": "bar", "baz": 42, "qux": true]), // Changed attribute order - (["foo": "bar", "baz": 42, "qux": true], - ["qux": true, "foo": "bar", "baz": 42]), + ("{ \"qux\": true, \"foo\": \"bar\", \"baz\": 42 }", + ["foo": "bar", "baz": 42, "qux": true]), + // Newlines and indentations + ("{ \"foo\": \"bar\", \n\"baz\": 42, \"qux\": true }", + ["foo": "bar", "baz": 42, "qux": true]), // Nested objects - (["foo": "bar", "baz": ["qux": true, "quux": ["spam", "ham", "eggs"]]], + ("{ \"foo\": \"bar\", \"baz\": { \"qux\": true, \"quux\": [\"spam\", \"ham\", \"eggs\"] } }", ["foo": "bar", "baz": ["qux": true, "quux": ["spam", "ham", "eggs"]]]), // Nested objects with changed attribute order - (["foo": "bar", "baz": ["qux": true, "quux": ["spam", "ham", "eggs"]]], - ["foo": "bar", "baz": ["quux": ["spam", "ham", "eggs"], "qux": true]]), + ("{ \"foo\": \"bar\", \"baz\": { \"quux\": [\"spam\", \"ham\", \"eggs\"], \"qux\": true } }", + ["foo": "bar", "baz": ["qux": true, "quux": ["spam", "ham", "eggs"]]]), ] - for (jsonObject, expectedJsonObject) in jsonObjects { + for (jsonString, expectedJsonObject) in jsonStringsAndObjects { var req = URLRequest(url: URL(string: "foo://bar")!) - - let data = try! JSONSerialization.data(withJSONObject: jsonObject, options: []) - req.httpBody = data - + req.httpBody = jsonString.data(using: .utf8) let matchesJsonBody = hasJsonBody(expectedJsonObject)(req) XCTAssertTrue(matchesJsonBody) @@ -486,33 +486,30 @@ class SwiftHelpersTests : XCTestCase { #if swift(>=3.0) func testHasJsonBodyIsFalse() { - let jsonObjects = [ + let jsonStringsAndObjects = [ // Changed value - (["foo": "bar"], + ("{ \"foo\": \"bar\" }", ["foo": "qux"]), // Changed key - (["foo": "bar"], + ("{ \"foo\": \"bar\" }", ["baz": "bar"]), // Missing attribute - (["foo": "bar", "baz": 42], - ["baz": "bar"]), + ("{ \"foo\": \"bar\", \"baz\": 42 }", + ["foo": "bar"]), // Extraneous attribute - (["foo": "bar"], + ("{ \"foo\": \"bar\" }", ["foo": "bar", "baz": 42]), // Changed order in array - (["foo": ["spam", "ham", "eggs"]], + ("{ \"foo\": [\"spam\", \"ham\", \"eggs\"] }", ["foo": ["spam", "eggs", "ham"]]), // Nested objects with changed value - (["foo": "bar", "baz": ["qux": true]], + ("{ \"foo\": \"bar\", \"baz\": { \"qux\": true } }", ["foo": "bar", "baz": ["qux": false]]) ] - for (jsonObject, expectedJsonObject) in jsonObjects { + for (jsonString, expectedJsonObject) in jsonStringsAndObjects { var req = URLRequest(url: URL(string: "foo://bar")!) - - let data = try! JSONSerialization.data(withJSONObject: jsonObject, options: []) - req.httpBody = data - + req.httpBody = jsonString.data(using: .utf8) let matchesJsonBody = hasJsonBody(expectedJsonObject)(req) XCTAssertFalse(matchesJsonBody)