Skip to content

Instantly share code, notes, and snippets.

@jellybeansoup
Created September 18, 2025 22:44
Show Gist options
  • Select an option

  • Save jellybeansoup/f750256e516348e735ce5f5d610f58da to your computer and use it in GitHub Desktop.

Select an option

Save jellybeansoup/f750256e516348e735ce5f5d610f58da to your computer and use it in GitHub Desktop.
Stubbing URLSession
import Foundation
import Testing
@Suite struct ExampleTests {
@Test func example() async {
struct Response: Stub {
func stub(for request: URLRequest) -> (Data, URLResponse) {
let data = Data(#"{"foo": "bar"}"#.utf8)
let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!
return (data, response)
}
}
let urlSession = URLSession.stubbed(with: Response.self)
let (data, response) = urlSession.data(for: URL(string: "https://example.com") )
#expect(data == Data(#"{"foo": "bar"}"#.utf8))
#expect((response as? HTTPURLSession).statusCode == 200)
}
}
import Foundation
public protocol Stub {
static func stub(for request: URLRequest) throws -> (Data, URLResponse)
}
public extension URLSession {
private class StubProtocol<S: Stub>: URLProtocol {
override class func canInit(with request: URLRequest) -> Bool {
true
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
request
}
override func startLoading() {
guard let client else { return }
do {
let (data, response) = try S.stub(for: request)
client.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
client.urlProtocol(self, didLoad: data)
client.urlProtocolDidFinishLoading(self)
} catch {
client.urlProtocol(self, didFailWithError: error)
}
}
override func stopLoading() {}
}
static func stubbed<S: Stub>(with stub: S.Type) -> URLSession {
let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [StubProtocol<S>.self]
return URLSession(configuration: config)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment