Просмотр исходного кода

Guard Against Invalid Objects in JSON Encoding (#3624)

* Guard against invalid objects.

* Add and modernize encoding tests.

* Remove old 5.7 workaround.

* Formatting.

* Fix “used within initial value” error on older versions.

* Fix nightly build.

* Run thread sanitizer tests on Firebreak.
Jon Shier 3 лет назад
Родитель
Сommit
38d07f329a

+ 2 - 2
.github/workflows/ci.yml

@@ -165,7 +165,7 @@ jobs:
         run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -project "Alamofire.xcodeproj" -scheme "Alamofire watchOS" -destination "${{ matrix.destination }}" clean test | xcpretty
   Thread_Sanitizer:
     name: Test Thread Sanitizer
-    runs-on: macOS-12
+    runs-on: firebreak
     env:
       DEVELOPER_DIR: "/Applications/Xcode_13.4.1.app/Contents/Developer"
     timeout-minutes: 10
@@ -192,7 +192,7 @@ jobs:
     steps:
       - uses: actions/checkout@v3
       - name: Install Firewalk
-        run: brew install alamofire/alamofire/firewalk && firewalk &
+        run: brew install alamofire/alamofire/firewalk || brew upgrade alamofire/alamofire/firewalk && firewalk &
       - name: ${{ matrix.name }}
         run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -project "Alamofire.xcodeproj" -scheme "${{ matrix.scheme }}" -destination "${{ matrix.destination }}" -testPlan "${{ matrix.testPlan }}" clean test | xcpretty
   SPM:

+ 21 - 0
Source/ParameterEncoding.swift

@@ -239,6 +239,10 @@ public struct URLEncoding: ParameterEncoding {
 /// Uses `JSONSerialization` to create a JSON representation of the parameters object, which is set as the body of the
 /// request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`.
 public struct JSONEncoding: ParameterEncoding {
+    public enum Error: Swift.Error {
+        case invalidJSONObject
+    }
+
     // MARK: Properties
 
     /// Returns a `JSONEncoding` instance with default writing options.
@@ -266,6 +270,10 @@ public struct JSONEncoding: ParameterEncoding {
 
         guard let parameters = parameters else { return urlRequest }
 
+        guard JSONSerialization.isValidJSONObject(parameters) else {
+            throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: Error.invalidJSONObject))
+        }
+
         do {
             let data = try JSONSerialization.data(withJSONObject: parameters, options: options)
 
@@ -294,6 +302,10 @@ public struct JSONEncoding: ParameterEncoding {
 
         guard let jsonObject = jsonObject else { return urlRequest }
 
+        guard JSONSerialization.isValidJSONObject(jsonObject) else {
+            throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: Error.invalidJSONObject))
+        }
+
         do {
             let data = try JSONSerialization.data(withJSONObject: jsonObject, options: options)
 
@@ -310,6 +322,15 @@ public struct JSONEncoding: ParameterEncoding {
     }
 }
 
+extension JSONEncoding.Error {
+    public var localizedDescription: String {
+        """
+        Invalid JSON object provided for parameter or object encoding. \
+        This is most likely due to a value which can't be represented in Objective-C.
+        """
+    }
+}
+
 // MARK: -
 
 extension NSNumber {

+ 5 - 5
Tests/CacheTests.swift

@@ -138,9 +138,9 @@ final class CacheTestCase: BaseTestCase {
 
     @discardableResult
     private func startRequest(cacheControl: CacheControl,
-                      cachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy,
-                      queue: DispatchQueue = .main,
-                      completion: @escaping (URLRequest?, HTTPURLResponse?) -> Void)
+                              cachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy,
+                              queue: DispatchQueue = .main,
+                              completion: @escaping (URLRequest?, HTTPURLResponse?) -> Void)
         -> URLRequest {
         let urlRequest = Endpoint(path: .cache,
                                   timeout: 30,
@@ -158,8 +158,8 @@ final class CacheTestCase: BaseTestCase {
     // MARK: - Test Execution and Verification
 
     private func executeTest(cachePolicy: URLRequest.CachePolicy,
-                     cacheControl: CacheControl,
-                     shouldReturnCachedResponse: Bool) {
+                             cacheControl: CacheControl,
+                             shouldReturnCachedResponse: Bool) {
         // Given
         let requestDidFinish = expectation(description: "cache test request did finish")
         var response: HTTPURLResponse?

+ 2 - 2
Tests/ConcurrencyTests.swift

@@ -498,12 +498,12 @@ final class ClosureAPIConcurrencyTests: BaseTestCase {
                      tasks: [URLSessionTask],
                      descriptions: [String],
                      response: AFDataResponse<TestResponse>)
-        #if swift(>=5.7)
+        #if swift(>=5.8)
         values = try! await (uploadProgress, downloadProgress, requests, tasks, descriptions, response)
         #else
         values = await (uploadProgress, downloadProgress, requests, tasks, descriptions, response)
         #endif
-        
+
         // Then
         XCTAssertTrue(values.uploadProgresses.isEmpty)
         XCTAssertNotNil(values.downloadProgresses.last)

+ 397 - 561
Tests/ParameterEncodingTests.swift

@@ -39,627 +39,475 @@ final class URLParameterEncodingTestCase: ParameterEncodingTestCase {
 
     // MARK: Tests - Parameter Types
 
-    func testURLParameterEncodeNilParameters() {
-        do {
-            // Given, When
-            let urlRequest = try encoding.encode(self.urlRequest, with: nil)
-
-            // Then
-            XCTAssertNil(urlRequest.url?.query)
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+    func testURLParameterEncodeNilParameters() throws {
+        // Given, When
+        let urlRequest = try encoding.encode(self.urlRequest, with: nil)
+
+        // Then
+        XCTAssertNil(urlRequest.url?.query)
     }
 
-    func testURLParameterEncodeEmptyDictionaryParameter() {
-        do {
-            // Given
-            let parameters: [String: Any] = [:]
+    func testURLParameterEncodeEmptyDictionaryParameter() throws {
+        // Given
+        let parameters: [String: Any] = [:]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertNil(urlRequest.url?.query)
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertNil(urlRequest.url?.query)
     }
 
-    func testURLParameterEncodeOneStringKeyStringValueParameter() {
-        do {
-            // Given
-            let parameters = ["foo": "bar"]
+    func testURLParameterEncodeOneStringKeyStringValueParameter() throws {
+        // Given
+        let parameters = ["foo": "bar"]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "foo=bar")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "foo=bar")
     }
 
-    func testURLParameterEncodeOneStringKeyStringValueParameterAppendedToQuery() {
-        do {
-            // Given
-            var mutableURLRequest = self.urlRequest
-            var urlComponents = URLComponents(url: mutableURLRequest.url!, resolvingAgainstBaseURL: false)!
-            urlComponents.query = "baz=qux"
-            mutableURLRequest.url = urlComponents.url
+    func testURLParameterEncodeOneStringKeyStringValueParameterAppendedToQuery() throws {
+        // Given
+        var mutableURLRequest = self.urlRequest
+        var urlComponents = URLComponents(url: mutableURLRequest.url!, resolvingAgainstBaseURL: false)!
+        urlComponents.query = "baz=qux"
+        mutableURLRequest.url = urlComponents.url
 
-            let parameters = ["foo": "bar"]
+        let parameters = ["foo": "bar"]
 
-            // When
-            let urlRequest = try encoding.encode(mutableURLRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(mutableURLRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "baz=qux&foo=bar")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "baz=qux&foo=bar")
     }
 
-    func testURLParameterEncodeTwoStringKeyStringValueParameters() {
-        do {
-            // Given
-            let parameters = ["foo": "bar", "baz": "qux"]
+    func testURLParameterEncodeTwoStringKeyStringValueParameters() throws {
+        // Given
+        let parameters = ["foo": "bar", "baz": "qux"]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "baz=qux&foo=bar")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "baz=qux&foo=bar")
     }
 
-    func testURLParameterEncodeStringKeyNSNumberIntegerValueParameter() {
-        do {
-            // Given
-            let parameters = ["foo": NSNumber(value: 25)]
+    func testURLParameterEncodeStringKeyNSNumberIntegerValueParameter() throws {
+        // Given
+        let parameters = ["foo": NSNumber(value: 25)]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "foo=25")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "foo=25")
     }
 
-    func testURLParameterEncodeStringKeyNSNumberBoolValueParameter() {
-        do {
-            // Given
-            let parameters = ["foo": NSNumber(value: false)]
+    func testURLParameterEncodeStringKeyNSNumberBoolValueParameter() throws {
+        // Given
+        let parameters = ["foo": NSNumber(value: false)]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "foo=0")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "foo=0")
     }
 
-    func testURLParameterEncodeStringKeyIntegerValueParameter() {
-        do {
-            // Given
-            let parameters = ["foo": 1]
+    func testURLParameterEncodeStringKeyIntegerValueParameter() throws {
+        // Given
+        let parameters = ["foo": 1]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "foo=1")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "foo=1")
     }
 
-    func testURLParameterEncodeStringKeyDoubleValueParameter() {
-        do {
-            // Given
-            let parameters = ["foo": 1.1]
+    func testURLParameterEncodeStringKeyDoubleValueParameter() throws {
+        // Given
+        let parameters = ["foo": 1.1]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "foo=1.1")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "foo=1.1")
     }
 
-    func testURLParameterEncodeStringKeyBoolValueParameter() {
-        do {
-            // Given
-            let parameters = ["foo": true]
+    func testURLParameterEncodeStringKeyBoolValueParameter() throws {
+        // Given
+        let parameters = ["foo": true]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "foo=1")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "foo=1")
     }
 
-    func testURLParameterEncodeStringKeyArrayValueParameter() {
-        do {
-            // Given
-            let parameters = ["foo": ["a", 1, true]]
+    func testURLParameterEncodeStringKeyArrayValueParameter() throws {
+        // Given
+        let parameters = ["foo": ["a", 1, true]]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "foo%5B%5D=a&foo%5B%5D=1&foo%5B%5D=1")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "foo%5B%5D=a&foo%5B%5D=1&foo%5B%5D=1")
     }
 
-    func testURLParameterEncodeArrayNestedDictionaryValueParameterWithIndex() {
-        do {
-            // Given
-            let encoding = URLEncoding(arrayEncoding: .indexInBrackets)
-            let parameters = ["foo": ["a", 1, true, ["bar": 2], ["qux": 3], ["quy": ["quz": 3]]]]
+    func testURLParameterEncodeArrayNestedDictionaryValueParameterWithIndex() throws {
+        // Given
+        let encoding = URLEncoding(arrayEncoding: .indexInBrackets)
+        let parameters = ["foo": ["a", 1, true, ["bar": 2], ["qux": 3], ["quy": ["quz": 3]]]]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "foo%5B0%5D=a&foo%5B1%5D=1&foo%5B2%5D=1&foo%5B3%5D%5Bbar%5D=2&foo%5B4%5D%5Bqux%5D=3&foo%5B5%5D%5Bquy%5D%5Bquz%5D=3")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "foo%5B0%5D=a&foo%5B1%5D=1&foo%5B2%5D=1&foo%5B3%5D%5Bbar%5D=2&foo%5B4%5D%5Bqux%5D=3&foo%5B5%5D%5Bquy%5D%5Bquz%5D=3")
     }
 
-    func testURLParameterEncodeStringKeyArrayValueParameterWithoutBrackets() {
-        do {
-            // Given
-            let encoding = URLEncoding(arrayEncoding: .noBrackets)
-            let parameters = ["foo": ["a", 1, true]]
+    func testURLParameterEncodeStringKeyArrayValueParameterWithoutBrackets() throws {
+        // Given
+        let encoding = URLEncoding(arrayEncoding: .noBrackets)
+        let parameters = ["foo": ["a", 1, true]]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "foo=a&foo=1&foo=1")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "foo=a&foo=1&foo=1")
     }
 
-    func testURLParameterEncodeStringKeyDictionaryValueParameter() {
-        do {
-            // Given
-            let parameters = ["foo": ["bar": 1]]
+    func testURLParameterEncodeStringKeyDictionaryValueParameter() throws {
+        // Given
+        let parameters = ["foo": ["bar": 1]]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "foo%5Bbar%5D=1")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "foo%5Bbar%5D=1")
     }
 
-    func testURLParameterEncodeStringKeyNestedDictionaryValueParameter() {
-        do {
-            // Given
-            let parameters = ["foo": ["bar": ["baz": 1]]]
+    func testURLParameterEncodeStringKeyNestedDictionaryValueParameter() throws {
+        // Given
+        let parameters = ["foo": ["bar": ["baz": 1]]]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "foo%5Bbar%5D%5Bbaz%5D=1")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "foo%5Bbar%5D%5Bbaz%5D=1")
     }
 
-    func testURLParameterEncodeStringKeyNestedDictionaryArrayValueParameter() {
-        do {
-            // Given
-            let parameters = ["foo": ["bar": ["baz": ["a", 1, true]]]]
+    func testURLParameterEncodeStringKeyNestedDictionaryArrayValueParameter() throws {
+        // Given
+        let parameters = ["foo": ["bar": ["baz": ["a", 1, true]]]]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            let expectedQuery = "foo%5Bbar%5D%5Bbaz%5D%5B%5D=a&foo%5Bbar%5D%5Bbaz%5D%5B%5D=1&foo%5Bbar%5D%5Bbaz%5D%5B%5D=1"
-            XCTAssertEqual(urlRequest.url?.query, expectedQuery)
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        let expectedQuery = "foo%5Bbar%5D%5Bbaz%5D%5B%5D=a&foo%5Bbar%5D%5Bbaz%5D%5B%5D=1&foo%5Bbar%5D%5Bbaz%5D%5B%5D=1"
+        XCTAssertEqual(urlRequest.url?.query, expectedQuery)
     }
 
-    func testURLParameterEncodeStringKeyNestedDictionaryArrayValueParameterWithoutBrackets() {
-        do {
-            // Given
-            let encoding = URLEncoding(arrayEncoding: .noBrackets)
-            let parameters = ["foo": ["bar": ["baz": ["a", 1, true]]]]
+    func testURLParameterEncodeStringKeyNestedDictionaryArrayValueParameterWithoutBrackets() throws {
+        // Given
+        let encoding = URLEncoding(arrayEncoding: .noBrackets)
+        let parameters = ["foo": ["bar": ["baz": ["a", 1, true]]]]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            let expectedQuery = "foo%5Bbar%5D%5Bbaz%5D=a&foo%5Bbar%5D%5Bbaz%5D=1&foo%5Bbar%5D%5Bbaz%5D=1"
-            XCTAssertEqual(urlRequest.url?.query, expectedQuery)
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        let expectedQuery = "foo%5Bbar%5D%5Bbaz%5D=a&foo%5Bbar%5D%5Bbaz%5D=1&foo%5Bbar%5D%5Bbaz%5D=1"
+        XCTAssertEqual(urlRequest.url?.query, expectedQuery)
     }
 
-    func testURLParameterLiteralBoolEncodingWorksAndDoesNotAffectNumbers() {
-        do {
-            // Given
-            let encoding = URLEncoding(boolEncoding: .literal)
-            let parameters: [String: Any] = [// Must still encode to numbers
-                "a": 1,
-                "b": 0,
-                "c": 1.0,
-                "d": 0.0,
-                "e": NSNumber(value: 1),
-                "f": NSNumber(value: 0),
-                "g": NSNumber(value: 1.0),
-                "h": NSNumber(value: 0.0),
-
-                // Must encode to literals
-                "i": true,
-                "j": false,
-                "k": NSNumber(value: true),
-                "l": NSNumber(value: false)]
-
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
-
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "a=1&b=0&c=1&d=0&e=1&f=0&g=1&h=0&i=true&j=false&k=true&l=false")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+    func testURLParameterLiteralBoolEncodingWorksAndDoesNotAffectNumbers() throws {
+        // Given
+        let encoding = URLEncoding(boolEncoding: .literal)
+        let parameters: [String: Any] = [// Must still encode to numbers
+            "a": 1,
+            "b": 0,
+            "c": 1.0,
+            "d": 0.0,
+            "e": NSNumber(value: 1),
+            "f": NSNumber(value: 0),
+            "g": NSNumber(value: 1.0),
+            "h": NSNumber(value: 0.0),
+
+            // Must encode to literals
+            "i": true,
+            "j": false,
+            "k": NSNumber(value: true),
+            "l": NSNumber(value: false)]
+
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "a=1&b=0&c=1&d=0&e=1&f=0&g=1&h=0&i=true&j=false&k=true&l=false")
     }
 
     // MARK: Tests - All Reserved / Unreserved / Illegal Characters According to RFC 3986
 
-    func testThatReservedCharactersArePercentEscapedMinusQuestionMarkAndForwardSlash() {
-        do {
-            // Given
-            let generalDelimiters = ":#[]@"
-            let subDelimiters = "!$&'()*+,;="
-            let parameters = ["reserved": "\(generalDelimiters)\(subDelimiters)"]
-
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
-
-            // Then
-            let expectedQuery = "reserved=%3A%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D"
-            XCTAssertEqual(urlRequest.url?.query, expectedQuery)
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+    func testThatReservedCharactersArePercentEscapedMinusQuestionMarkAndForwardSlash() throws {
+        // Given
+        let generalDelimiters = ":#[]@"
+        let subDelimiters = "!$&'()*+,;="
+        let parameters = ["reserved": "\(generalDelimiters)\(subDelimiters)"]
+
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+
+        // Then
+        let expectedQuery = "reserved=%3A%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D"
+        XCTAssertEqual(urlRequest.url?.query, expectedQuery)
     }
 
-    func testThatReservedCharactersQuestionMarkAndForwardSlashAreNotPercentEscaped() {
-        do {
-            // Given
-            let parameters = ["reserved": "?/"]
+    func testThatReservedCharactersQuestionMarkAndForwardSlashAreNotPercentEscaped() throws {
+        // Given
+        let parameters = ["reserved": "?/"]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "reserved=?/")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "reserved=?/")
     }
 
-    func testThatUnreservedNumericCharactersAreNotPercentEscaped() {
-        do {
-            // Given
-            let parameters = ["numbers": "0123456789"]
+    func testThatUnreservedNumericCharactersAreNotPercentEscaped() throws {
+        // Given
+        let parameters = ["numbers": "0123456789"]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "numbers=0123456789")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "numbers=0123456789")
     }
 
-    func testThatUnreservedLowercaseCharactersAreNotPercentEscaped() {
-        do {
-            // Given
-            let parameters = ["lowercase": "abcdefghijklmnopqrstuvwxyz"]
+    func testThatUnreservedLowercaseCharactersAreNotPercentEscaped() throws {
+        // Given
+        let parameters = ["lowercase": "abcdefghijklmnopqrstuvwxyz"]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "lowercase=abcdefghijklmnopqrstuvwxyz")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "lowercase=abcdefghijklmnopqrstuvwxyz")
     }
 
-    func testThatUnreservedUppercaseCharactersAreNotPercentEscaped() {
-        do {
-            // Given
-            let parameters = ["uppercase": "ABCDEFGHIJKLMNOPQRSTUVWXYZ"]
+    func testThatUnreservedUppercaseCharactersAreNotPercentEscaped() throws {
+        // Given
+        let parameters = ["uppercase": "ABCDEFGHIJKLMNOPQRSTUVWXYZ"]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "uppercase=ABCDEFGHIJKLMNOPQRSTUVWXYZ")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "uppercase=ABCDEFGHIJKLMNOPQRSTUVWXYZ")
     }
 
-    func testThatIllegalASCIICharactersArePercentEscaped() {
-        do {
-            // Given
-            let parameters = ["illegal": " \"#%<>[]\\^`{}|"]
+    func testThatIllegalASCIICharactersArePercentEscaped() throws {
+        // Given
+        let parameters = ["illegal": " \"#%<>[]\\^`{}|"]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            let expectedQuery = "illegal=%20%22%23%25%3C%3E%5B%5D%5C%5E%60%7B%7D%7C"
-            XCTAssertEqual(urlRequest.url?.query, expectedQuery)
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        let expectedQuery = "illegal=%20%22%23%25%3C%3E%5B%5D%5C%5E%60%7B%7D%7C"
+        XCTAssertEqual(urlRequest.url?.query, expectedQuery)
     }
 
     // MARK: Tests - Special Character Queries
 
-    func testURLParameterEncodeStringWithAmpersandKeyStringWithAmpersandValueParameter() {
-        do {
-            // Given
-            let parameters = ["foo&bar": "baz&qux", "foobar": "bazqux"]
+    func testURLParameterEncodeStringWithAmpersandKeyStringWithAmpersandValueParameter() throws {
+        // Given
+        let parameters = ["foo&bar": "baz&qux", "foobar": "bazqux"]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "foo%26bar=baz%26qux&foobar=bazqux")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "foo%26bar=baz%26qux&foobar=bazqux")
     }
 
-    func testURLParameterEncodeStringWithQuestionMarkKeyStringWithQuestionMarkValueParameter() {
-        do {
-            // Given
-            let parameters = ["?foo?": "?bar?"]
+    func testURLParameterEncodeStringWithQuestionMarkKeyStringWithQuestionMarkValueParameter() throws {
+        // Given
+        let parameters = ["?foo?": "?bar?"]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "?foo?=?bar?")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "?foo?=?bar?")
     }
 
-    func testURLParameterEncodeStringWithSlashKeyStringWithQuestionMarkValueParameter() {
-        do {
-            // Given
-            let parameters = ["foo": "/bar/baz/qux"]
+    func testURLParameterEncodeStringWithSlashKeyStringWithQuestionMarkValueParameter() throws {
+        // Given
+        let parameters = ["foo": "/bar/baz/qux"]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "foo=/bar/baz/qux")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "foo=/bar/baz/qux")
     }
 
-    func testURLParameterEncodeStringWithSpaceKeyStringWithSpaceValueParameter() {
-        do {
-            // Given
-            let parameters = [" foo ": " bar "]
+    func testURLParameterEncodeStringWithSpaceKeyStringWithSpaceValueParameter() throws {
+        // Given
+        let parameters = [" foo ": " bar "]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "%20foo%20=%20bar%20")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "%20foo%20=%20bar%20")
     }
 
-    func testURLParameterEncodeStringWithPlusKeyStringWithPlusValueParameter() {
-        do {
-            // Given
-            let parameters = ["+foo+": "+bar+"]
+    func testURLParameterEncodeStringWithPlusKeyStringWithPlusValueParameter() throws {
+        // Given
+        let parameters = ["+foo+": "+bar+"]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "%2Bfoo%2B=%2Bbar%2B")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "%2Bfoo%2B=%2Bbar%2B")
     }
 
-    func testURLParameterEncodeStringKeyPercentEncodedStringValueParameter() {
-        do {
-            // Given
-            let parameters = ["percent": "%25"]
+    func testURLParameterEncodeStringKeyPercentEncodedStringValueParameter() throws {
+        // Given
+        let parameters = ["percent": "%25"]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "percent=%2525")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "percent=%2525")
     }
 
-    func testURLParameterEncodeStringKeyNonLatinStringValueParameter() {
-        do {
-            // Given
-            let parameters = ["french": "français",
-                              "japanese": "日本語",
-                              "arabic": "العربية",
-                              "emoji": "😃"]
+    func testURLParameterEncodeStringKeyNonLatinStringValueParameter() throws {
+        // Given
+        let parameters = ["french": "français",
+                          "japanese": "日本語",
+                          "arabic": "العربية",
+                          "emoji": "😃"]
 
-            // When
-            let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(self.urlRequest, with: parameters)
 
-            // Then
-            let expectedParameterValues = ["arabic=%D8%A7%D9%84%D8%B9%D8%B1%D8%A8%D9%8A%D8%A9",
-                                           "emoji=%F0%9F%98%83",
-                                           "french=fran%C3%A7ais",
-                                           "japanese=%E6%97%A5%E6%9C%AC%E8%AA%9E"]
+        // Then
+        let expectedParameterValues = ["arabic=%D8%A7%D9%84%D8%B9%D8%B1%D8%A8%D9%8A%D8%A9",
+                                       "emoji=%F0%9F%98%83",
+                                       "french=fran%C3%A7ais",
+                                       "japanese=%E6%97%A5%E6%9C%AC%E8%AA%9E"]
 
-            let expectedQuery = expectedParameterValues.joined(separator: "&")
-            XCTAssertEqual(urlRequest.url?.query, expectedQuery)
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        let expectedQuery = expectedParameterValues.joined(separator: "&")
+        XCTAssertEqual(urlRequest.url?.query, expectedQuery)
     }
 
-    func testURLParameterEncodeStringForRequestWithPrecomposedQuery() {
-        do {
-            // Given
-            let url = URL(string: "https://example.com/movies?hd=[1]")!
-            let parameters = ["page": "0"]
+    func testURLParameterEncodeStringForRequestWithPrecomposedQuery() throws {
+        // Given
+        let url = URL(string: "https://example.com/movies?hd=[1]")!
+        let parameters = ["page": "0"]
 
-            // When
-            let urlRequest = try encoding.encode(URLRequest(url: url), with: parameters)
+        // When
+        let urlRequest = try encoding.encode(URLRequest(url: url), with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "hd=%5B1%5D&page=0")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "hd=%5B1%5D&page=0")
     }
 
-    func testURLParameterEncodeStringWithPlusKeyStringWithPlusValueParameterForRequestWithPrecomposedQuery() {
-        do {
-            // Given
-            let url = URL(string: "https://example.com/movie?hd=[1]")!
-            let parameters = ["+foo+": "+bar+"]
+    func testURLParameterEncodeStringWithPlusKeyStringWithPlusValueParameterForRequestWithPrecomposedQuery() throws {
+        // Given
+        let url = URL(string: "https://example.com/movie?hd=[1]")!
+        let parameters = ["+foo+": "+bar+"]
 
-            // When
-            let urlRequest = try encoding.encode(URLRequest(url: url), with: parameters)
+        // When
+        let urlRequest = try encoding.encode(URLRequest(url: url), with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "hd=%5B1%5D&%2Bfoo%2B=%2Bbar%2B")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "hd=%5B1%5D&%2Bfoo%2B=%2Bbar%2B")
     }
 
-    func testURLParameterEncodeStringWithThousandsOfChineseCharacters() {
-        do {
-            // Given
-            let repeatedCount = 2000
-            let url = URL(string: "https://example.com/movies")!
-            let parameters = ["chinese": String(repeating: "一二三四五六七八九十", count: repeatedCount)]
+    func testURLParameterEncodeStringWithThousandsOfChineseCharacters() throws {
+        // Given
+        let repeatedCount = 2000
+        let url = URL(string: "https://example.com/movies")!
+        let parameters = ["chinese": String(repeating: "一二三四五六七八九十", count: repeatedCount)]
 
-            // When
-            let urlRequest = try encoding.encode(URLRequest(url: url), with: parameters)
+        // When
+        let urlRequest = try encoding.encode(URLRequest(url: url), with: parameters)
 
-            // Then
-            var expected = "chinese="
+        // Then
+        var expected = "chinese="
 
-            for _ in 0..<repeatedCount {
-                expected += "%E4%B8%80%E4%BA%8C%E4%B8%89%E5%9B%9B%E4%BA%94%E5%85%AD%E4%B8%83%E5%85%AB%E4%B9%9D%E5%8D%81"
-            }
-
-            XCTAssertEqual(urlRequest.url?.query, expected)
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
+        for _ in 0..<repeatedCount {
+            expected += "%E4%B8%80%E4%BA%8C%E4%B8%89%E5%9B%9B%E4%BA%94%E5%85%AD%E4%B8%83%E5%85%AB%E4%B9%9D%E5%8D%81"
         }
+
+        XCTAssertEqual(urlRequest.url?.query, expected)
     }
 
     // MARK: Tests - Varying HTTP Methods
 
-    func testThatURLParameterEncodingEncodesGETParametersInURL() {
-        do {
-            // Given
-            var mutableURLRequest = self.urlRequest
-            mutableURLRequest.httpMethod = HTTPMethod.get.rawValue
-            let parameters = ["foo": 1, "bar": 2]
-
-            // When
-            let urlRequest = try encoding.encode(mutableURLRequest, with: parameters)
-
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "bar=2&foo=1")
-            XCTAssertNil(urlRequest.value(forHTTPHeaderField: "Content-Type"), "Content-Type should be nil")
-            XCTAssertNil(urlRequest.httpBody, "HTTPBody should be nil")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+    func testThatURLParameterEncodingEncodesGETParametersInURL() throws {
+        // Given
+        var mutableURLRequest = self.urlRequest
+        mutableURLRequest.httpMethod = HTTPMethod.get.rawValue
+        let parameters = ["foo": 1, "bar": 2]
+
+        // When
+        let urlRequest = try encoding.encode(mutableURLRequest, with: parameters)
+
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "bar=2&foo=1")
+        XCTAssertNil(urlRequest.value(forHTTPHeaderField: "Content-Type"), "Content-Type should be nil")
+        XCTAssertNil(urlRequest.httpBody, "HTTPBody should be nil")
     }
 
-    func testThatURLParameterEncodingEncodesPOSTParametersInHTTPBody() {
-        do {
-            // Given
-            var mutableURLRequest = self.urlRequest
-            mutableURLRequest.httpMethod = HTTPMethod.post.rawValue
-            let parameters = ["foo": 1, "bar": 2]
+    func testThatURLParameterEncodingEncodesPOSTParametersInHTTPBody() throws {
+        // Given
+        var mutableURLRequest = self.urlRequest
+        mutableURLRequest.httpMethod = HTTPMethod.post.rawValue
+        let parameters = ["foo": 1, "bar": 2]
 
-            // When
-            let urlRequest = try encoding.encode(mutableURLRequest, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(mutableURLRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.value(forHTTPHeaderField: "Content-Type"), "application/x-www-form-urlencoded; charset=utf-8")
-            XCTAssertNotNil(urlRequest.httpBody, "HTTPBody should not be nil")
+        // Then
+        XCTAssertEqual(urlRequest.value(forHTTPHeaderField: "Content-Type"), "application/x-www-form-urlencoded; charset=utf-8")
+        XCTAssertNotNil(urlRequest.httpBody, "HTTPBody should not be nil")
 
-            if let httpBody = urlRequest.httpBody, let decodedHTTPBody = String(data: httpBody, encoding: .utf8) {
-                XCTAssertEqual(decodedHTTPBody, "bar=2&foo=1")
-            } else {
-                XCTFail("decoded http body should not be nil")
-            }
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        XCTAssertEqual(urlRequest.httpBody?.asString, "bar=2&foo=1")
     }
 
-    func testThatURLEncodedInURLParameterEncodingEncodesPOSTParametersInURL() {
-        do {
-            // Given
-            var mutableURLRequest = self.urlRequest
-            mutableURLRequest.httpMethod = HTTPMethod.post.rawValue
-            let parameters = ["foo": 1, "bar": 2]
+    func testThatURLEncodedInURLParameterEncodingEncodesPOSTParametersInURL() throws {
+        // Given
+        var mutableURLRequest = self.urlRequest
+        mutableURLRequest.httpMethod = HTTPMethod.post.rawValue
+        let parameters = ["foo": 1, "bar": 2]
 
-            // When
-            let urlRequest = try URLEncoding.queryString.encode(mutableURLRequest, with: parameters)
+        // When
+        let urlRequest = try URLEncoding.queryString.encode(mutableURLRequest, with: parameters)
 
-            // Then
-            XCTAssertEqual(urlRequest.url?.query, "bar=2&foo=1")
-            XCTAssertNil(urlRequest.value(forHTTPHeaderField: "Content-Type"))
-            XCTAssertNil(urlRequest.httpBody, "HTTPBody should be nil")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "bar=2&foo=1")
+        XCTAssertNil(urlRequest.value(forHTTPHeaderField: "Content-Type"))
+        XCTAssertNil(urlRequest.httpBody, "HTTPBody should be nil")
     }
 }
 
@@ -672,103 +520,91 @@ final class JSONParameterEncodingTestCase: ParameterEncodingTestCase {
 
     // MARK: Tests
 
-    func testJSONParameterEncodeNilParameters() {
-        do {
-            // Given, When
-            let URLRequest = try encoding.encode(urlRequest, with: nil)
-
-            // Then
-            XCTAssertNil(URLRequest.url?.query, "query should be nil")
-            XCTAssertNil(URLRequest.value(forHTTPHeaderField: "Content-Type"))
-            XCTAssertNil(URLRequest.httpBody, "HTTPBody should be nil")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+    func testJSONParameterEncodeNilParameters() throws {
+        // Given, When
+        let request = try encoding.encode(urlRequest, with: nil)
+
+        // Then
+        XCTAssertNil(request.url?.query, "query should be nil")
+        XCTAssertNil(request.value(forHTTPHeaderField: "Content-Type"))
+        XCTAssertNil(request.httpBody, "HTTPBody should be nil")
     }
 
-    func testJSONParameterEncodeComplexParameters() {
-        do {
-            // Given
-            let parameters: [String: Any] = ["foo": "bar",
-                                             "baz": ["a", 1, true],
-                                             "qux": ["a": 1,
-                                                     "b": [2, 2],
-                                                     "c": [3, 3, 3]]]
-
-            // When
-            let URLRequest = try encoding.encode(urlRequest, with: parameters)
-
-            // Then
-            XCTAssertNil(URLRequest.url?.query)
-            XCTAssertNotNil(URLRequest.value(forHTTPHeaderField: "Content-Type"))
-            XCTAssertEqual(URLRequest.value(forHTTPHeaderField: "Content-Type"), "application/json")
-            XCTAssertNotNil(URLRequest.httpBody)
-
-            if let httpBody = URLRequest.httpBody {
-                do {
-                    let json = try JSONSerialization.jsonObject(with: httpBody, options: .allowFragments)
-
-                    if let json = json as? NSObject {
-                        XCTAssertEqual(json, parameters as NSObject)
-                    } else {
-                        XCTFail("json should be an NSObject")
-                    }
-                } catch {
-                    XCTFail("json should not be nil")
-                }
-            }
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+    func testJSONParameterEncodeComplexParameters() throws {
+        // Given
+        let parameters: [String: Any] = ["foo": "bar",
+                                         "baz": ["a", 1, true],
+                                         "qux": ["a": 1,
+                                                 "b": [2, 2],
+                                                 "c": [3, 3, 3]]]
+
+        // When
+        let request = try encoding.encode(urlRequest, with: parameters)
+
+        // Then
+        XCTAssertNil(request.url?.query)
+        XCTAssertNotNil(request.value(forHTTPHeaderField: "Content-Type"))
+        XCTAssertEqual(request.value(forHTTPHeaderField: "Content-Type"), "application/json")
+        XCTAssertNotNil(request.httpBody)
+
+        XCTAssertEqual(try request.httpBody?.asJSONObject() as? NSObject,
+                       parameters as NSObject,
+                       "Decoded request body and parameters should be equal.")
     }
 
-    func testJSONParameterEncodeArray() {
-        do {
-            // Given
-            let array: [String] = ["foo", "bar", "baz"]
-
-            // When
-            let URLRequest = try encoding.encode(urlRequest, withJSONObject: array)
-
-            // Then
-            XCTAssertNil(URLRequest.url?.query)
-            XCTAssertNotNil(URLRequest.value(forHTTPHeaderField: "Content-Type"))
-            XCTAssertEqual(URLRequest.value(forHTTPHeaderField: "Content-Type"), "application/json")
-            XCTAssertNotNil(URLRequest.httpBody)
-
-            if let httpBody = URLRequest.httpBody {
-                do {
-                    let json = try JSONSerialization.jsonObject(with: httpBody, options: .allowFragments)
-
-                    if let json = json as? NSObject {
-                        XCTAssertEqual(json, array as NSObject)
-                    } else {
-                        XCTFail("json should be an NSObject")
-                    }
-                } catch {
-                    XCTFail("json should not be nil")
-                }
-            }
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+    func testJSONParameterEncodeArray() throws {
+        // Given
+        let array = ["foo", "bar", "baz"]
+
+        // When
+        let request = try encoding.encode(urlRequest, withJSONObject: array)
+
+        // Then
+        XCTAssertNil(request.url?.query)
+        XCTAssertNotNil(request.value(forHTTPHeaderField: "Content-Type"))
+        XCTAssertEqual(request.value(forHTTPHeaderField: "Content-Type"), "application/json")
+        XCTAssertNotNil(request.httpBody)
+
+        XCTAssertEqual(try request.httpBody?.asJSONObject() as? NSObject,
+                       array as NSObject,
+                       "Decoded request body and parameters should be equal.")
     }
 
-    func testJSONParameterEncodeParametersRetainsCustomContentType() {
-        do {
-            // Given
-            let request = Endpoint(headers: [.contentType("application/custom-json-type+json")]).urlRequest
+    func testJSONParameterEncodeParametersRetainsCustomContentType() throws {
+        // Given
+        let request = Endpoint(headers: [.contentType("application/custom-json-type+json")]).urlRequest
 
-            let parameters = ["foo": "bar"]
+        let parameters = ["foo": "bar"]
 
-            // When
-            let urlRequest = try encoding.encode(request, with: parameters)
+        // When
+        let urlRequest = try encoding.encode(request, with: parameters)
 
-            // Then
-            XCTAssertNil(urlRequest.url?.query)
-            XCTAssertEqual(urlRequest.headers["Content-Type"], "application/custom-json-type+json")
-        } catch {
-            XCTFail("Test encountered unexpected error: \(error)")
-        }
+        // Then
+        XCTAssertNil(urlRequest.url?.query)
+        XCTAssertEqual(urlRequest.headers["Content-Type"], "application/custom-json-type+json")
+    }
+
+    func testJSONParameterEncodeParametersThrowsErrorWithInvalidValue() {
+        // Given
+        struct Value {}
+        let value = Value()
+
+        // When
+        let result = Result { try encoding.encode(urlRequest, with: ["key": value]) }
+
+        // Then
+        XCTAssertTrue(result.failure?.asAFError?.isJSONEncodingFailed == true)
+    }
+
+    func testJSONParameterEncodeObjectThrowsErrorWithInvalidValue() {
+        // Given
+        struct Value {}
+        let value = Value()
+
+        // When
+        let result = Result { try encoding.encode(urlRequest, withJSONObject: value) }
+
+        // Then
+        XCTAssertTrue(result.failure?.asAFError?.isJSONEncodingFailed == true)
     }
 }

+ 4 - 0
Tests/TestHelpers.swift

@@ -386,6 +386,10 @@ extension Data {
     var asString: String {
         String(decoding: self, as: UTF8.self)
     }
+
+    func asJSONObject() throws -> Any {
+        try JSONSerialization.jsonObject(with: self, options: .allowFragments)
+    }
 }
 
 struct TestResponse: Decodable {