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) diff --git a/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift b/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift index e32df03c..963ac88a 100644 --- a/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift +++ b/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift @@ -392,6 +392,26 @@ 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 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) + } +} +#endif + // MARK: Operators on OHHTTPStubsTestBlock /** diff --git a/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift b/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift index 3c366db4..3b636466 100644 --- a/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift +++ b/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift @@ -454,6 +454,69 @@ class SwiftHelpersTests : XCTestCase { #endif } +#if swift(>=3.0) + func testHasJsonBodyIsTrue() { + let jsonStringsAndObjects = [ + // Exact match + ("{ \"foo\": \"bar\", \"baz\": 42, \"qux\": true }", + ["foo": "bar", "baz": 42, "qux": true]), + // Changed attribute order + ("{ \"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"]]]), + // Nested objects with changed attribute order + ("{ \"foo\": \"bar\", \"baz\": { \"quux\": [\"spam\", \"ham\", \"eggs\"], \"qux\": true } }", + ["foo": "bar", "baz": ["qux": true, "quux": ["spam", "ham", "eggs"]]]), + ] + + for (jsonString, expectedJsonObject) in jsonStringsAndObjects { + var req = URLRequest(url: URL(string: "foo://bar")!) + req.httpBody = jsonString.data(using: .utf8) + let matchesJsonBody = hasJsonBody(expectedJsonObject)(req) + + XCTAssertTrue(matchesJsonBody) + } + } +#endif + +#if swift(>=3.0) + func testHasJsonBodyIsFalse() { + let jsonStringsAndObjects = [ + // Changed value + ("{ \"foo\": \"bar\" }", + ["foo": "qux"]), + // Changed key + ("{ \"foo\": \"bar\" }", + ["baz": "bar"]), + // Missing attribute + ("{ \"foo\": \"bar\", \"baz\": 42 }", + ["foo": "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 (jsonString, expectedJsonObject) in jsonStringsAndObjects { + var req = URLRequest(url: URL(string: "foo://bar")!) + req.httpBody = jsonString.data(using: .utf8) + let matchesJsonBody = hasJsonBody(expectedJsonObject)(req) + + XCTAssertFalse(matchesJsonBody) + } + } +#endif + let sampleURLs = [ // Absolute URLs "scheme:",