Mocker

repository·master·Indexed 22 days ago

https://github.com/wetransfer/mocker

A Swift library for offline data request unit testing that uses a custom URLProtocol to intercept and mock network traffic. It supports JSON mocks, file extension matching, request delays, and network error simulation. Compatible with URLSession and Alamofire, Mocker provides tools for verifying requests via callbacks and XCTest expectations.

Tokens
2.9K
Snippets
13
Records
14
Agent score
29%

What's inside Mocker

  1. Install Mocker using Swift Package Manager

    master

    You can add Mocker to your project using Swift Package Manager via two methods:

    Via Package.swift

    Add the Mocker repository URL as a dependency in your Package.swift file and include it in the dependencies of your target:

    import PackageDescription
    
    let package = Package(
        name: "MyProject",
        platforms: [
           .macOS(.v10_15)
        ],
        dependencies: [
            .package(url: "https://github.com/WeTransfer/Mocker.git", .upToNextMajor(from: "3.0.0"))
        ],
        targets: [
            .target(
                name: "MyProject",
                dependencies: ["Mocker"]),
            .testTarget(
                name: "MyProjectTests",
                dependencies: ["MyProject"]),
        ]
    )

    Via Xcode UI

    1. Select File > Swift Packages > Add Package Dependency.
    2. Enter the repository URL: https://github.com/WeTransfer/Mocker.git.
  2. Install Mocker manually as an embedded framework

    master

    If you are not using a dependency manager, follow these steps to integrate Mocker manually:

    1. Initialize Git (if not already a repository):
      $ git init
    2. Add as a Submodule:
      $ git submodule add https://github.com/WeTransfer/Mocker.git
    3. Add to Xcode Project:
      • Open the new Mocker folder and drag Mocker.xcodeproj into your application's Xcode Project Navigator.
      • Select Mocker.xcodeproj and ensure its deployment target matches your application's target.
    4. Embed the Framework:
      • Select your application project (blue icon) in the Project Navigator.
      • Select your application target under Targets.
      • Open the General panel.
      • Under Embedded Binaries, click the + button and select Mocker.framework.

    Once completed, Mocker.framework is automatically added as a target dependency, linked framework, and embedded framework for both simulator and device builds.

    $ git init
    $ git submodule add https://github.com/WeTransfer/Mocker.git
  3. Activate the Mocker for URLSession and Alamofire

    master

    Mocker automatically activates for the default URL loading system (like URLSession.shared) once the first Mock is registered.

    If you are using custom URLSession instances or Alamofire, you must manually register MockingURLProtocol in your configuration's protocolClasses.

    // For custom URLSessions
    let configuration = URLSessionConfiguration.default
    configuration.protocolClasses = [MockingURLProtocol.self]
    let urlSession = URLSession(configuration: configuration)
    
    // For Alamofire
    let configuration = URLSessionConfiguration.af.default
    configuration.protocolClasses = [MockingURLProtocol.self]
    let sessionManager = Alamofire.Session(configuration: configuration)
  4. Install Mocker using Carthage

    master

    To integrate Mocker using Carthage, add the following line to your Cartfile:

    github "WeTransfer/Mocker" ~> 3.0.0

    Then, run carthage update to build the framework and drag the resulting Mocker.framework into your Xcode project.

  5. Configure Mocker mode (Opt-in vs Opt-out)

    master

    You can change how Mocker behaves globally using Mocker.mode:

    • .optout (Default): Mocker intercepts all requests. If a request doesn't match a registered mock, it may cause issues.
    • .optin: Mocker only intercepts requests that match a registered mock, ignoring everything else.
    // Only catch mocked URLs and ignore every other URL
    Mocker.mode = .optin
    
    // Set back to default behavior
    Mocker.mode = .optout
  6. Resolve XCTest build errors in Swift Package Manager

    master

    If you encounter the error cannot find auto-link library XCTest and XCTestSwiftSupport when using Swift Package Manager, you must update your build settings:

    1. Go to your project's Build Options.
    2. Locate the property ENABLE_TESTING_SEARCH_PATHS.
    3. Change its value from No to Yes.
  7. Register a JSON Mock request

    master

    To mock a specific URL with a JSON response, create a Mock object specifying the URL, contentType as .json, the desired statusCode, and a dictionary mapping HTTP methods (like .get) to the response Data.

    let originalURL = URL(string: "https://www.wetransfer.com/example.json")!
        
    let mock = Mock(url: originalURL, contentType: .json, statusCode: 200, data: [
        .get : try! Data(contentsOf: MockedData.exampleJSON)
    ])
    mock.register()
  8. Add delays to mocked responses

    master

    To test request cancellation or loading states, you can add a delay to a Mock using the delay property with a DispatchTimeInterval.

    let exampleURL = URL(string: "https://www.wetransfer.com/api/endpoint")!
    
    var mock = Mock(url: exampleURL, contentType: .json, statusCode: 200, data: [
        .head: try! Data(contentsOf: MockedData.headResponse),
        .get: try! Data(contentsOf: MockedData.exampleJSON)
    ])
    mock.delay = DispatchTimeInterval.seconds(5)
    mock.register()
  9. Use Mock callbacks and expectations

    master

    Mocker provides hooks to verify that requests are being made correctly:

    1. onRequestHandler: A callback that provides access to the request and the parsed HTTP body arguments.
    2. completion: A closure called when the mock request completes.
    3. Expectations: Helper functions expectationForRequestingMock(&mock) and expectationForCompletingMock(&mock) to integrate with XCTest expectations.
    // Using callbacks
    var mock = Mock(url: request.url!, contentType: .json, statusCode: 200, data: [.post: Data()])
    mock.onRequestHandler = OnRequestHandler(httpBodyType: [[String:String]].self, callback: { request, postBodyArguments in
        XCTAssertEqual(request.url, mock.request.url)
        XCTAssertEqual(expectedParameters, postBodyArguments)
        onRequestExpectation.fulfill()
    })
    mock.completion = { 
        endpointIsCalledExpectation.fulfill() 
    }
    mock.register()
    
    // Using XCTest expectations
    var mock = Mock(url: url, contentType: .json, statusCode: 200, data: [.get: Data()])
    let requestExpectation = expectationForRequestingMock(&mock)
    let completionExpectation = expectationForCompletingMock(&mock)
    mock.register()
    
    URLSession.shared.dataTask(with: URLRequest(url: url)).resume()
    wait(for: [requestExpectation, completionExpectation], timeout: 2.0)
  10. Mock network errors

    master

    To test error handling logic in your application, you can configure a Mock to return a specific error using the requestError parameter.

    Mock(url: originalURL, contentType: .json, statusCode: 500, data: [.get: Data()],
         requestError: TestExampleError.example).register()
  11. Ignore specific URLs in Mocker

    master

    By default, Mocker catches all URLs. To prevent it from intercepting specific requests (which might cause fatalError if no mock exists), use Mocker.ignore(). You can specify the matching strategy using matchType.

    Available match types:

    • Exact match (default)
    • .ignoreQuery: Matches URL while ignoring query parameters.
    • .prefix: Matches any URL that begins with the specified string.
    let ignoredURL = URL(string: "https://www.wetransfer.com")!
    
    // Ignore any requests that exactly match the URL
    Mocker.ignore(ignoredURL)
    
    // Ignore any requests that match the URL, with any query parameters
    Mocker.ignore(ignoredURL, matchType: .ignoreQuery)
    
    // Ignore any requests that begin with the URL
    Mocker.ignore(ignoredURL, matchType: .prefix)