How to mock a protocol with generics

I have the following protocol that a NetworkEngine conforms to:

protocol NetworkEngineProtocol {
    func request<T: Decodable>(from endpoint: Endpoint) async throws -> T
}

This protocol is passed as a dependency into a custom ViewModel and I want to unit test the request method of the ViewModel, that itself calls the dependency request method.

class ViewModel: ViewModelProtocol {
    var passthrough: PassthroughSubject<[SomeProperty], Never> = PassthroughSubject<[SomeProperty], Never>()
    private let networkEngine: NetworkEngineProtocol
    
    init(networkEngine: NetworkEngineProtocol) {
        self.networkEngine = networkEngine
    }

    func request() {
        Task {
            do {
                let model = try await networkEngine.request(from: .messages()) as CustomModel                
                // ... do more stuff
                passthrough.send(someValue) // would want to test this
            } catch let err as NetworkEngineErrors {
                // ... error handling
            }            
        }
    }
}

struct CustomModel: Decodable {
    let message: [String]
    let status: String    
}

In order to Unit Test this method, I'd have to create a Mock class, that conforms to the NetworkEngineProtocol.

Something like:

class MockNetworkProtocol: NetworkEngineProtocol {
    init() {}

    func request<T>(from endpoint: Endpoint) async throws -> T where T : Decodable {
        // how to mock this?
    }
}

What's the best way to do this? How can I mock the request method of the NetworkEngine?

Thanks

Replies

How can I mock the request method of the NetworkEngine?

It’s hard to say without knowing more about your Endpoint type. If, say, Endpoint was an enum like this:

enum Endpoint {
    case messages
    … other cases …
}

you could write this:

func request<T>(from endpoint: Endpoint) async throws -> T where T : Decodable {
    let response: Data
    switch endpoint {
    case .messages:
        response = Data("""
            … your response here …
            """.utf8)
    … other cases …
    }
    return try JSONDecoder().decode(T.self, from: response)
}

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"