Brightify / Cuckoo

Boilerplate-free mocking framework for Swift!
MIT License
1.67k stars 174 forks source link

'Generic parameter 'T' could not be inferred' when stubbing protocol with generics #401

Open seboslaw opened 3 years ago

seboslaw commented 3 years ago

Hey guys,

I'm facing problems when setting up stubbing for the following protocol:

protocol KeyValueStorageProtocol {
    func load<T>(_ key: String, type: T.Type, completion: @escaping ((T) -> Void), failure: @escaping ((Error) -> Void)) where T: Codable
}

I'm currently saving Date objects in that Storage, so my test setup is as follows:

let mock = MockKeyValueStorageProtocol()
stub(mock) { stub in
  when(stub.load(any(), type: any(), completion: anyClosure(), failure: anyClosure()))
  .then({
    $2(Date())
  })
}

However, I'm getting the following error for the when(stub... line:

Generic parameter 'T' could not be inferred

So I'm not sure if providing the type (Date) in the .then command is enough to make the when(stub... resolve the generic type? I'm also not sure, I'm using the block syntax correctly here? I'm trying to make calls to the load function return a date in the completion block.

Any help or hints would be highly appreciated!

chaseklingelzen commented 3 months ago

Did you ever find a solution to this problem? I'm experiencing the same thing.

chaseklingelzen commented 3 months ago

Believe I figured it out in case this helps someone in the future:

/// A protocol that defines the interface for managing active tasks for a specific type of GraphQL query.
public protocol ActiveTaskManager {
    /// Retrieves an existing task for the given key or creates and stores a new task if none exists.
    /// - Parameters:
    ///   - key: The unique key identifying the task.
    ///   - newTask: A closure that creates the new task if no existing task is found. This closure must be `@escaping`
    ///     to be stored and called later, and `@Sendable` to ensure it can be safely executed concurrently.
    /// - Returns: The existing or newly created task associated with the given key.
    func getOrSetTask<T: GraphQLQuery>(
        for key: String,
        queryType: T.Type,
        newTask: @escaping @Sendable () async throws -> ServerResponse<T.Response>
    ) async -> Task<ServerResponse<T.Response>, Error>

    /// Removes the task associated with the given key.
    /// - Parameter key: The unique key identifying the task to be removed.
    func removeTask(for key: String) async
}

// how to stub in tests:

stub(activeTaskManagerProvider) { stub in
       stub.getManager(for: any(PlaidLinkTokenQuery.Type.self)).thenReturn(DefaultActiveTasksManager())
}
MatyasKriz commented 3 months ago

@chaseklingelzen so specifying the type explicitly in the any(_:) matcher makes the code compilable and Cuckoo is able to verify the method's calls?

chaseklingelzen commented 3 months ago

@MatyasKriz! Exactly. I think using any is the key here. If you're running into issues, feel free to add a code snippet and I'll do my best to see if I can determine what your issue is.

MatyasKriz commented 3 months ago

Oh I was just curious, no issues here. 🙂 Thanks for your investigation.