diff --git a/Sources/HTTPMock/Internal/HTTPMockLog.swift b/Sources/HTTPMock/Internal/HTTPMockLog.swift index adebbca..260ddc8 100644 --- a/Sources/HTTPMock/Internal/HTTPMockLog.swift +++ b/Sources/HTTPMock/Internal/HTTPMockLog.swift @@ -7,49 +7,54 @@ public enum HTTPMockLog { public static var level: Level = .trace public static func error(_ message: @autoclosure () -> String) { - if should(.error) { + let level = Level.error + if shouldLog(level) { let message = message() logger.error("[HTTPMock][\(level.description)] \(message, privacy: .public)") - printToConsole("⛔️ \(message)", level: .error) + printToConsole("⛔️ \(message)", level: level) } } public static func warning(_ message: @autoclosure () -> String) { - if should(.warning) { + let level = Level.warning + if shouldLog(level) { let message = message() logger.warning("[HTTPMock][\(level.description)] \(message, privacy: .public)") - printToConsole("⚠️ \(message)", level: .warning) + printToConsole("⚠️ \(message)", level: level) } } public static func info(_ message: @autoclosure () -> String) { - if should(.info) { + let level = Level.info + if shouldLog(level) { let message = message() logger.info("[HTTPMock][\(level.description)] \(message, privacy: .public)") - printToConsole(message, level: .info) + printToConsole(message, level: level) } } public static func debug(_ message: @autoclosure () -> String) { - if should(.debug) { + let level = Level.debug + if shouldLog(level) { let message = message() logger.debug("[HTTPMock][\(level.description)] \(message, privacy: .public)") - printToConsole(message, level: .debug) + printToConsole(message, level: level) } } public static func trace(_ message: @autoclosure () -> String) { - if should(.trace) { + let level = Level.trace + if shouldLog(level) { let message = message() logger.log("[HTTPMock][\(level.description)] \(message, privacy: .public)") - printToConsole(message, level: .trace) + printToConsole(message, level: level) } } // MARK: - Private methods @inline(__always) - private static func should(_ l: Level) -> Bool { + private static func shouldLog(_ l: Level) -> Bool { isEnabled && l.rawValue <= level.rawValue } diff --git a/Sources/HTTPMock/Internal/HTTPMockURLProtocol.swift b/Sources/HTTPMock/Internal/HTTPMockURLProtocol.swift index f807ce7..9fc6632 100644 --- a/Sources/HTTPMock/Internal/HTTPMockURLProtocol.swift +++ b/Sources/HTTPMock/Internal/HTTPMockURLProtocol.swift @@ -230,6 +230,11 @@ final class HTTPMockURLProtocol: URLProtocol { } task.resume() + case .throwError(let errorToThrow): + HTTPMockLog.info("No mock found for incoming request '\(requestDescription)' — throwing provided error") + + client?.urlProtocol(self, didFailWithError: errorToThrow as NSError) + case .fatalError: HTTPMockLog.info("No mock found for incoming request '\(requestDescription)' — performing a fatalError") diff --git a/Sources/HTTPMock/Models/UnmockedPolicy.swift b/Sources/HTTPMock/Models/UnmockedPolicy.swift index a0a7c84..c5d603f 100644 --- a/Sources/HTTPMock/Models/UnmockedPolicy.swift +++ b/Sources/HTTPMock/Models/UnmockedPolicy.swift @@ -11,7 +11,10 @@ public enum UnmockedPolicy { /// Let the request pass through to the internet. /// Useful for integration tests where you want to mock some responses, but let others hit actual network. case passthrough - + + /// Throw the specified error when an unmocked request is encountered. + case throwError(Error) + /// Perform a `fatalError()` call to abruptly end the running app/test. /// Useful for strict testing of your networking. case fatalError diff --git a/Tests/HTTPMockTests/HTTPMockTests.swift b/Tests/HTTPMockTests/HTTPMockTests.swift index c237789..0a2a8c5 100644 --- a/Tests/HTTPMockTests/HTTPMockTests.swift +++ b/Tests/HTTPMockTests/HTTPMockTests.swift @@ -147,6 +147,20 @@ struct HTTPMockTests { #expect(data3.toString == "Different mock") } + @Test + func unmockedPolicy_throwError() async throws { + let url = try #require(URL(string: "https://example.com/throw-error")) + + httpMock.unmockedPolicy = .throwError(UnmockedError.wasThrown) + + do { + _ = try await httpMock.urlSession.data(from: url) + Issue.record("Expected error not thrown") + } catch { + #expect((error as NSError).domain == UnmockedError.errorDomain) + } + } + @Test func itDoesNotRegisterTheKeyIfNoResponsesAreProvided() { let key = makeKey() @@ -664,3 +678,10 @@ struct HTTPMockTests { ) } } + +private enum UnmockedError: Int, Error, CustomNSError { + case wasThrown = 666 + + static var errorDomain: String { "HTTPMock.UnmockedError" } + var errorCode: Int { rawValue } +}