Skip to content

Commit

Permalink
Enable post body checks (#41)
Browse files Browse the repository at this point in the history
* Enable post body checks

* Improve documentation
  • Loading branch information
AvdLee authored Feb 7, 2020
1 parent b030402 commit 09d6e60
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 4 deletions.
24 changes: 23 additions & 1 deletion MockerTests/MockerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ final class MockerTests: XCTestCase {
let onRequestExpectation = expectation(description: "Data request should start")
let completionExpectation = expectation(description: "Data request should succeed")
var mock = Mock(dataType: .json, statusCode: 200, data: [.get: Data()])
mock.onRequest = {
mock.onRequest = { _, _ in
onRequestExpectation.fulfill()
}
mock.completion = {
Expand All @@ -281,6 +281,28 @@ final class MockerTests: XCTestCase {
wait(for: [onRequestExpectation, completionExpectation], timeout: 2.0, enforceOrder: true)
}

/// It should report post body arguments if they exist.
func testOnRequestPostBodyParameters() throws {
let onRequestExpectation = expectation(description: "Data request should start")

let expectedParameters = ["test": "value"]
var request = URLRequest(url: URL(string: "https://www.fakeurl.com")!)
request.httpMethod = Mock.HTTPMethod.post.rawValue
request.httpBody = try JSONSerialization.data(withJSONObject: expectedParameters, options: .prettyPrinted)

var mock = Mock(url: request.url!, dataType: .json, statusCode: 200, data: [.post: Data()])
mock.onRequest = { request, postBodyArguments in
XCTAssertEqual(request.url, mock.request.url)
XCTAssertEqual(expectedParameters, postBodyArguments as? [String: String])
onRequestExpectation.fulfill()
}
mock.register()

URLSession.shared.dataTask(with: request).resume()

wait(for: [onRequestExpectation], timeout: 2.0)
}

/// It should call the mock after a delay.
func testDelayedMock() {
let nonDelayExpectation = expectation(description: "Data request should succeed")
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Mocker is a library written in Swift which makes it possible to mock data reques
- [Delayed responses](#delayed-responses)
- [Redirect responses](#redirect-responses)
- [Ignoring URLs](#ignoring-urls)
- [Mock callbacks](#mock-callbacks)
- [Communication](#communication)
- [Installation](#installation)
- [Release Notes](#release-notes)
Expand Down Expand Up @@ -186,6 +187,23 @@ let ignoredURL = URL(string: "www.wetransfer.com")!
Mocker.ignore(ignoredURL)
```

##### Mock callbacks
You can register on `Mock` callbacks to make testing easier.

```swift
var mock = Mock(url: request.url!, dataType: .json, statusCode: 200, data: [.post: Data()])
mock.onRequest = { request, postBodyArguments in
XCTAssertEqual(request.url, mock.request.url)
XCTAssertEqual(expectedParameters, postBodyArguments as? [String: String])
onRequestExpectation.fulfill()
}
mock.completion = {
endpointIsCalledExpectation.fulfill()
}
mock.register()
```


## Communication

- If you **found a bug**, open an issue.
Expand Down
6 changes: 4 additions & 2 deletions Sources/Mock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Foundation

/// A Mock which can be used for mocking data requests with the `Mocker` by calling `Mocker.register(...)`.
public struct Mock: Equatable {

/// HTTP method definitions.
///
/// See https://tools.ietf.org/html/rfc7231#section-4.3
Expand Down Expand Up @@ -54,6 +54,8 @@ public struct Mock: Equatable {
}
}
}

public typealias OnRequest = (_ request: URLRequest, _ httpBodyArguments: [String: Any]?) -> Void

/// The type of the data which is returned.
public let dataType: DataType
Expand Down Expand Up @@ -97,7 +99,7 @@ public struct Mock: Equatable {
public var completion: (() -> Void)?

/// The callback which will be executed everytime this `Mock` was started. Can be used within unit tests for validating that a request has been started.
public var onRequest: (() -> Void)?
public var onRequest: OnRequest?

private init(url: URL? = nil, ignoreQuery: Bool = false, dataType: DataType, statusCode: Int, data: [HTTPMethod: Data], additionalHeaders: [String: String] = [:], fileExtensions: [String]? = nil) {
self.urlToMock = url
Expand Down
33 changes: 32 additions & 1 deletion Sources/MockingURLProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ public final class MockingURLProtocol: URLProtocol {
return
}

mock.onRequest?()
if let onRequest = mock.onRequest {
onRequest(request, request.postBodyArguments)
}

guard let delay = mock.delay else {
finishRequest(for: mock, data: data, response: response)
Expand Down Expand Up @@ -97,3 +99,32 @@ private extension Data {
return redirectLocation
}
}

private extension URLRequest {
var postBodyArguments: [String: Any]? {
guard let httpBody = httpBodyStreamData() else { return nil }
return try? JSONSerialization.jsonObject(with: httpBody, options: .fragmentsAllowed) as? [String: Any]
}

/// We need to use the http body stream data as the URLRequest once launched converts the `httpBody` to this stream of data.
private func httpBodyStreamData() -> Data? {
guard let bodyStream = self.httpBodyStream else { return nil }

bodyStream.open()

// Will read 16 chars per iteration. Can use bigger buffer if needed
let bufferSize: Int = 16
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
var data = Data()

while bodyStream.hasBytesAvailable {
let readData = bodyStream.read(buffer, maxLength: bufferSize)
data.append(buffer, count: readData)
}

buffer.deallocate()
bodyStream.close()

return data
}
}

0 comments on commit 09d6e60

Please sign in to comment.