Skip to content

Commit

Permalink
Merge pull request #3 from WeTransfer/feature/improvements
Browse files Browse the repository at this point in the history
Mocker improvements
  • Loading branch information
AvdLee authored Nov 3, 2017
2 parents ece822f + 67ff201 commit 82391ec
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 47 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
source "https://rubygems.org"

gem 'danger'
gem 'danger-swiftlint'
gem 'danger-swiftlint', '0.9.0'
gem 'danger-xcov'
gem 'danger-xcodebuild'
gem 'danger-xcode_summary'
Expand Down
2 changes: 1 addition & 1 deletion Mocker.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |spec|
spec.name = 'Mocker'
spec.version = '1.0.0'
spec.version = '1.1.0'
spec.summary = 'Mock data requests using a custom URLProtocol and run them offline.'
spec.description = 'Mocker is a library written in Swift which makes it possible to mock data requests using a custom URLProtocol and run them offline.'

Expand Down
24 changes: 19 additions & 5 deletions Mocker.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -177,17 +177,19 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0830;
LastUpgradeCheck = 0830;
LastUpgradeCheck = 0900;
ORGANIZATIONNAME = WeTransfer;
TargetAttributes = {
501E26931F3DAE370048F39E = {
CreatedOnToolsVersion = 8.3.3;
DevelopmentTeam = GLJTT4T544;
LastSwiftMigration = 0900;
ProvisioningStyle = Automatic;
};
501E269C1F3DAE370048F39E = {
CreatedOnToolsVersion = 8.3.3;
DevelopmentTeam = GLJTT4T544;
LastSwiftMigration = 0900;
ProvisioningStyle = Automatic;
};
};
Expand Down Expand Up @@ -285,15 +287,21 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
Expand Down Expand Up @@ -323,6 +331,7 @@
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
Expand All @@ -339,15 +348,21 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
Expand All @@ -369,6 +384,7 @@
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
Expand All @@ -391,7 +407,6 @@
PRODUCT_BUNDLE_IDENTIFIER = com.wetransfer.Mocker;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
};
name = Debug;
};
Expand All @@ -410,7 +425,6 @@
PRODUCT_BUNDLE_IDENTIFIER = com.wetransfer.Mocker;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
};
name = Release;
};
Expand All @@ -423,7 +437,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.wetransfer.MockerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_SWIFT3_OBJC_INFERENCE = On;
};
name = Debug;
};
Expand All @@ -436,7 +450,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.wetransfer.MockerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_SWIFT3_OBJC_INFERENCE = On;
};
name = Release;
};
Expand Down
4 changes: 3 additions & 1 deletion Mocker.xcodeproj/xcshareddata/xcschemes/Mocker.xcscheme
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0830"
LastUpgradeVersion = "0900"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand All @@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
Expand Down Expand Up @@ -56,6 +57,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
Expand Down
1 change: 1 addition & 0 deletions Mocker/MockerTests/.swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ disabled_rules: # Rule identifiers to exclude from running
- identifier_name
- force_cast
- force_try
- force_unwrapping
opt_in_rules:
- empty_count
- explicit_init
Expand Down
1 change: 1 addition & 0 deletions Mocker/MockerTests/MockedData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import UIKit
public final class MockedData {
public static let botAvatarImageFileUrl: URL = Bundle(for: MockedData.self).url(forResource: "wetransfer_bot_avater", withExtension: "png")!
public static let exampleJSON: URL = Bundle(for: MockedData.self).url(forResource: "Resources/JSON Files/example", withExtension: "json")!
public static let redirectGET: URL = Bundle(for: MockedData.self).url(forResource: "Resources/sample-redirect-get", withExtension: "data")!
}

internal extension URL {
Expand Down
70 changes: 63 additions & 7 deletions Mocker/MockerTests/MockerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ final class MockerTests: XCTestCase {
let expectation = self.expectation(description: "Data request should succeed")
let originalURL = URL(string: "https://avatars3.githubusercontent.com/u/26250426?v=4&s=400")!

let mock = Mock(url: originalURL, contentType: .imagePNG, statusCode: 200, data: [
let mock = Mock(url: originalURL, dataType: .imagePNG, statusCode: 200, data: [
.get: MockedData.botAvatarImageFileUrl.data
])

Expand All @@ -47,7 +47,7 @@ final class MockerTests: XCTestCase {
let expectation = self.expectation(description: "Data request should succeed")
let originalURL = URL(string: "https://www.wetransfer.com/sample-image.png")

Mock(fileExtensions: "png", contentType: .imagePNG, statusCode: 200, data: [
Mock(fileExtensions: "png", dataType: .imagePNG, statusCode: 200, data: [
.get: MockedData.botAvatarImageFileUrl.data
]).register()

Expand All @@ -68,7 +68,7 @@ final class MockerTests: XCTestCase {
let expectation = self.expectation(description: "Data request should succeed")
let originalURL = URL(string: "https://www.wetransfer.com/example.json")!

Mock(url: originalURL, contentType: .json, statusCode: 200, data: [
Mock(url: originalURL, dataType: .json, statusCode: 200, data: [
.get: MockedData.exampleJSON.data
]
).register()
Expand All @@ -95,7 +95,7 @@ final class MockerTests: XCTestCase {
func testAdditionalHeaders() {
let expectation = self.expectation(description: "Data request should succeed")
let headers = ["testkey": "testvalue"]
let mock = Mock(contentType: .json, statusCode: 200, data: [.get: Data()], additionalHeaders: headers)
let mock = Mock(dataType: .json, statusCode: 200, data: [.get: Data()], additionalHeaders: headers)
mock.register()

URLSession.shared.dataTask(with: mock.url) { (_, response, error) in
Expand All @@ -110,10 +110,10 @@ final class MockerTests: XCTestCase {
/// It should override existing mocks.
func testMockOverriding() {
let expectation = self.expectation(description: "Data request should succeed")
let mock = Mock(contentType: .json, statusCode: 200, data: [.get: Data()], additionalHeaders: ["testkey": "testvalue"])
let mock = Mock(dataType: .json, statusCode: 200, data: [.get: Data()], additionalHeaders: ["testkey": "testvalue"])
mock.register()

let newMock = Mock(contentType: .json, statusCode: 200, data: [.get: Data()], additionalHeaders: ["newkey": "newvalue"])
let newMock = Mock(dataType: .json, statusCode: 200, data: [.get: Data()], additionalHeaders: ["newkey": "newvalue"])
newMock.register()

URLSession.shared.dataTask(with: mock.url) { (_, response, error) in
Expand All @@ -130,7 +130,7 @@ final class MockerTests: XCTestCase {
let expectation = self.expectation(description: "Data request should succeed")
let originalURL = URL(string: "https://www.wetransfer.com/sample-image.png")

Mock(fileExtensions: "png", contentType: .imagePNG, statusCode: 200, data: [
Mock(fileExtensions: "png", dataType: .imagePNG, statusCode: 200, data: [
.get: MockedData.botAvatarImageFileUrl.data
]).register()

Expand All @@ -149,4 +149,60 @@ final class MockerTests: XCTestCase {

waitForExpectations(timeout: 10.0, handler: nil)
}

/// It should be possible to test cancellation of requests with a delayed mock.
func testDelayedMock() {
let expectation = self.expectation(description: "Data request should be cancelled")
var mock = Mock(dataType: .json, statusCode: 200, data: [.get: Data()])
mock.delay = DispatchTimeInterval.seconds(5)
mock.register()

let task = URLSession.shared.dataTask(with: mock.url) { (_, _, error) in
XCTAssert(error?._code == NSURLErrorCancelled)
expectation.fulfill()
}

task.resume()

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
task.cancel()
})

waitForExpectations(timeout: 10.0, handler: nil)
}

/// It should correctly handle redirect responses.
func testRedirectResponse() {
let expectation = self.expectation(description: "Data request should be cancelled")
let urlWhichRedirects: URL = URL(string: "https://we.tl/redirect")!
Mock(url: urlWhichRedirects, dataType: .html, statusCode: 200, data: [.get: MockedData.redirectGET.data]).register()
Mock(url: URL(string: "https://wetransfer.com/redirect")!, dataType: .json, statusCode: 200, data: [.get: MockedData.exampleJSON.data]).register()

URLSession.shared.dataTask(with: urlWhichRedirects) { (data, _, _) in

guard let data = data, let jsonDictionary = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] else {
XCTFail("Wrong data response")
expectation.fulfill()
return
}

let framework = Framework(jsonDictionary: jsonDictionary)
XCTAssert(framework.name == "Mocker")
XCTAssert(framework.owner == "WeTransfer")

expectation.fulfill()
}.resume()

waitForExpectations(timeout: 10.0, handler: nil)
}

/// It should be possible to ignore URLs and not let them be handled.
func testIgnoreURLs() {

let ignoredURL = URL(string: "www.wetransfer.com")!

XCTAssert(MockingURLProtocol.canInit(with: URLRequest(url: ignoredURL)) == true)
Mocker.ignore(ignoredURL)
XCTAssert(MockingURLProtocol.canInit(with: URLRequest(url: ignoredURL)) == false)
}
}
14 changes: 14 additions & 0 deletions Mocker/MockerTests/Resources/sample-redirect-get.data
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
HTTP/1.1 302 Moved Temporarily
Content-Type: text/html;charset=utf-8
Content-Length: 0
Cache-Control: public
Date: Tue, 10 Oct 2017 07:28:33 GMT
Location: https://wetransfer.com/redirect
Server: nginx/1.12.0
X-Content-Type-Options: nosniff
X-Request-Id: 8c43587ec891b2f1f72c61ecec2e96db
X-XSS-Protection: 1; mode=block
X-Cache: Miss from cloudfront
Via: 1.1 72f202fb973968c0cfdb028ab6f36fac.cloudfront.net (CloudFront)
X-Amz-Cf-Id: tU8eVZ9jWBJzd3aEB-4gyym_VxcPKskWFByEvXapy5WrdDkV-35-KA==
Connection: Keep-alive
48 changes: 45 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
</p>

<p align="center">
<img src="https://travis-ci.org/WeTransfer/Mocker.svg?branch=master"/>
<img src="https://api.travis-ci.org/WeTransfer/Mocker.svg?branch=master"/>
<img src="https://img.shields.io/cocoapods/v/Mocker.svg?style=flat"/>
<img src="https://img.shields.io/cocoapods/l/Mocker.svg?style=flat"/>
<img src="https://img.shields.io/cocoapods/p/Mocker.svg?style=flat"/>
<img src="https://img.shields.io/badge/language-swift4.0-f48041.svg?style=flat"/>
<img src="https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat"/>
<img src="https://img.shields.io/badge/License-MIT-yellow.svg?style=flat"/>
</p>
Expand All @@ -25,6 +26,9 @@ Mocker is a library written in Swift which makes it possible to mock data reques
- [JSON Requests](#json-requests)
- [File extensions](#file-extensions)
- [Custom HEAD and GET response](#custom-head-and-get-response)
- [Delayed responses](#delayed-responses)
- [Redirect responses](#redirect-responses)
- [Ignoring URLs](#ignoring-urls)
- [Communication](#communication)
- [Installation](#installation)
- [Release Notes](#release-notes)
Expand All @@ -39,9 +43,9 @@ _Run all your data request unit tests offline_ 🎉
- [x] Supports popular frameworks like `Alamofire`

## Requirements
- Swift 3.0, 3.1, 3.2
- Swift 3+
- iOS 8.0+
- Xcode 8.1, 8.2, 8.3
- Xcode 9.0+

## Usage

Expand Down Expand Up @@ -127,6 +131,44 @@ URLSession.shared.dataTask(with: exampleURL) { (data, response, error) in
}.resume()
```

##### Delayed responses
Sometimes you want to test if cancellation of requests is working. In that case, the mocked request should not finished directly and you need an delay. This can be added easily:

```swift
let exampleURL = URL(string: "https://www.wetransfer.com/api/endpoint")!

var mock = Mock(url: exampleURL, contentType: .json, statusCode: 200, data: [
.head: MockedData.headResponse.data,
.get: MockedData.exampleJSON.data
])
mock.delay = DispatchTimeInterval.seconds(5)
mock.register()
```

##### Redirect responses
Sometimes you want to mock short URLs or other redirect URLs. This is possible by saving the response and mock the redirect location, which can be found inside the response:

```
Date: Tue, 10 Oct 2017 07:28:33 GMT
Location: https://wetransfer.com/redirect
```

By creating a mock for the short URL and the redirect URL, you can mock redirect and test this behaviour:

```swift
let urlWhichRedirects: URL = URL(string: "https://we.tl/redirect")!
Mock(url: urlWhichRedirects, dataType: .html, statusCode: 200, data: [.get: MockedData.redirectGET.data]).register()
Mock(url: URL(string: "https://wetransfer.com/redirect")!, dataType: .json, statusCode: 200, data: [.get: MockedData.exampleJSON.data]).register()
```

##### Ignoring URLs
As the Mocker catches all URLs when registered, you might end up with a `fatalError` thrown in cases you don't need a mocked request. In that case you can ignore the URL:

```swift
let ignoredURL = URL(string: "www.wetransfer.com")!
Mocker.ignore(ignoredURL)
```

## Communication

- If you **found a bug**, open an issue.
Expand Down
Loading

0 comments on commit 82391ec

Please sign in to comment.