Skip to content

Commit

Permalink
Add XCTest extensions (#57)
Browse files Browse the repository at this point in the history
* Added expectations for on request and completion

* Updated the readme
  • Loading branch information
AvdLee authored May 29, 2020
1 parent 89a2234 commit 2128c42
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 18 deletions.
12 changes: 12 additions & 0 deletions Mocker.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
501B8FC4247E89C600B885F4 /* XCTest+Mocker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501B8FC3247E89C600B885F4 /* XCTest+Mocker.swift */; };
501E269E1F3DAE370048F39E /* Mocker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 501E26941F3DAE370048F39E /* Mocker.framework */; };
503446171F3DB4660039D5E4 /* Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503446141F3DB4660039D5E4 /* Mock.swift */; };
503446181F3DB4660039D5E4 /* Mocker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503446151F3DB4660039D5E4 /* Mocker.swift */; };
Expand All @@ -28,6 +29,8 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
501B8FC3247E89C600B885F4 /* XCTest+Mocker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCTest+Mocker.swift"; sourceTree = "<group>"; };
501B8FC5247E8BDE00B885F4 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
501E26941F3DAE370048F39E /* Mocker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Mocker.framework; sourceTree = BUILT_PRODUCTS_DIR; };
501E269D1F3DAE370048F39E /* MockerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MockerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
501F8B2D237594AC008EF77E /* Mocker.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = Mocker.podspec; sourceTree = "<group>"; };
Expand Down Expand Up @@ -65,6 +68,7 @@
501E268A1F3DAE370048F39E = {
isa = PBXGroup;
children = (
501B8FC5247E8BDE00B885F4 /* README.md */,
506277CC235F2777000A4316 /* Changelog.md */,
501F8B2D237594AC008EF77E /* Mocker.podspec */,
501E26951F3DAE370048F39E /* Products */,
Expand All @@ -89,6 +93,7 @@
503446141F3DB4660039D5E4 /* Mock.swift */,
503446151F3DB4660039D5E4 /* Mocker.swift */,
503446161F3DB4660039D5E4 /* MockingURLProtocol.swift */,
501B8FC3247E89C600B885F4 /* XCTest+Mocker.swift */,
);
path = Sources;
sourceTree = "<group>";
Expand Down Expand Up @@ -264,6 +269,7 @@
buildActionMask = 2147483647;
files = (
503446191F3DB4660039D5E4 /* MockingURLProtocol.swift in Sources */,
501B8FC4247E89C600B885F4 /* XCTest+Mocker.swift in Sources */,
503446181F3DB4660039D5E4 /* Mocker.swift in Sources */,
503446171F3DB4660039D5E4 /* Mock.swift in Sources */,
);
Expand Down Expand Up @@ -419,6 +425,9 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(PLATFORM_DIR)/Developer/Library/Frameworks\n$(PLATFORM_DIR)/Developer/Library/Frameworks",
);
INFOPLIST_FILE = "Mocker/Supporting Files/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
Expand All @@ -439,6 +448,9 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = (
"$(PLATFORM_DIR)/Developer/Library/Frameworks\n$(PLATFORM_DIR)/Developer/Library/Frameworks",
);
INFOPLIST_FILE = "Mocker/Supporting Files/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
Expand Down
27 changes: 26 additions & 1 deletion MockerTests/MockerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,32 @@ final class MockerTests: XCTestCase {
request.httpMethod = Mock.HTTPMethod.put.rawValue
XCTAssertNotNil(Mocker.mock(for: request))
}

/// It should call the on request expectation.
func testOnRequestExpectation() {
let url = URL(string: "https://www.fakeurl.com")!

var mock = Mock(url: url, dataType: .json, statusCode: 200, data: [.get: Data()])
let expectation = expectationForRequestingMock(&mock)
mock.register()

URLSession.shared.dataTask(with: URLRequest(url: url)).resume()

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

/// It should call the on completion expectation.
func testOnCompletionExpectation() {
let url = URL(string: "https://www.fakeurl.com")!

var mock = Mock(url: url, dataType: .json, statusCode: 200, data: [.get: Data()])
let expectation = expectationForCompletingMock(&mock)
mock.register()

URLSession.shared.dataTask(with: URLRequest(url: url)).resume()

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

/// it should return the error we requested from the mock when we pass in an Error.
func testMockReturningError() {
Expand Down Expand Up @@ -374,6 +400,5 @@ final class MockerTests: XCTestCase {
}.resume()

waitForExpectations(timeout: 10.0, handler: nil)

}
}
46 changes: 30 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,22 +188,6 @@ 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()
```

##### Mock errors

You can request a `Mock` to return an error, allowing testing of error handling.
Expand All @@ -227,6 +211,36 @@ URLSession.shared.dataTask(with: originalURL) { (data, urlresponse, err) in
}.resume()
```

##### 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()
```

##### Mock expectations
Instead of setting the `completion` and `onRequest` you can also make use of expectations:

```swift
var mock = Mock(url: url, dataType: .json, statusCode: 200, data: [.get: Data()])
let requestExpectation = expectationForCompletingMock(&mock)
let completionExpectation = expectationForCompletingMock(&mock)
mock.register()

URLSession.shared.dataTask(with: URLRequest(url: url)).resume()

wait(for: [requestExpectation, completionExpectation], timeout: 2.0)
```

## Communication

- If you **found a bug**, open an issue.
Expand Down
7 changes: 7 additions & 0 deletions Sources/Mock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
// swiftlint:disable force_unwrapping

import Foundation
import XCTest

/// A Mock which can be used for mocking data requests with the `Mocker` by calling `Mocker.register(...)`.
public struct Mock: Equatable {
Expand Down Expand Up @@ -103,6 +104,12 @@ public struct Mock: Equatable {

/// 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. The callback must be set before calling `register`.
public var onRequest: OnRequest?

/// Can only be set internally as it's used by the `expectationForRequestingMock(_:)` method.
var onRequestExpectation: XCTestExpectation?

/// Can only be set internally as it's used by the `expectationForCompletingMock(_:)` method.
var onCompletedExpectation: XCTestExpectation?

private init(url: URL? = nil, ignoreQuery: Bool = false, dataType: DataType, statusCode: Int, data: [HTTPMethod: Data], requestError: Error? = nil, additionalHeaders: [String: String] = [:], fileExtensions: [String]? = nil) {
self.urlToMock = url
Expand Down
2 changes: 2 additions & 0 deletions Sources/MockingURLProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public final class MockingURLProtocol: URLProtocol {
if let onRequest = mock.onRequest {
onRequest(request, request.postBodyArguments)
}
mock.onRequestExpectation?.fulfill()

guard let delay = mock.delay else {
finishRequest(for: mock, data: data, response: response)
Expand All @@ -72,6 +73,7 @@ public final class MockingURLProtocol: URLProtocol {
}

mock.completion?()
mock.onCompletedExpectation?.fulfill()
}

/// Implementation does nothing, but is needed for a valid inheritance of URLProtocol.
Expand Down
24 changes: 24 additions & 0 deletions Sources/XCTest+Mocker.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// XCTest+Mocker.swift
// Mocker
//
// Created by Antoine van der Lee on 27/05/2020.
// Copyright © 2020 WeTransfer. All rights reserved.
//

import Foundation
import XCTest

public extension XCTestCase {
func expectationForRequestingMock(_ mock: inout Mock) -> XCTestExpectation {
let mockExpectation = expectation(description: "\(mock) should be requested")
mock.onRequestExpectation = mockExpectation
return mockExpectation
}

func expectationForCompletingMock(_ mock: inout Mock) -> XCTestExpectation {
let mockExpectation = expectation(description: "\(mock) should be finishing")
mock.onCompletedExpectation = mockExpectation
return mockExpectation
}
}

0 comments on commit 2128c42

Please sign in to comment.