You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
Target: arm64-apple-macosx14.0
Problem
I'm building a generic networking stack and it's working really well. However I want to mock the API and I am a bit stuck generating a Mock for it through Sourcery. I did some stencil modifications before, for example to generate @published / Publisher pairs automatically, so I'm not unfamiliar with this, but at the moment I'm stuck.
While it's not hard to mock the code below manually, I would like to understand the problem a bit better and I like things to work in a standardized way.
First my protocol and model:
// sourcery: AutoMockable
publicprotocolNetworkHandling{@discardableResultfunc handle<Request:NetworkRequest>(request:Request)asyncthrows->Request.ResponseData}
// sourcery: AutoMockable
publicprotocolNetworkRequest{ // This one generates fine!
associatedtypeRequestData:Encodable=EmptyDataassociatedtypeResponseData:Decodable=EmptyDatavarpath:String{get}varneedsToken:Bool{get}varcontentType:ContentType{get}varmethod:HTTPMethod{get}varbody:RequestData{get}varqueryParameters:[String:String]?{get}varkeyDecodingStrategy:JSONDecoder.KeyDecodingStrategy{get}vardateDecodingStrategy:JSONDecoder.DateDecodingStrategy{get}varkeyEncodingStrategy:JSONEncoder.KeyEncodingStrategy{get}vardateEncodingStrategy:JSONEncoder.DateEncodingStrategy{get}}
The relevant part of my stencil:
{% macro methodName method %}func {{ method.shortName}}({%- for param in method.parameters %}{% if param.argumentLabel == nil %}_ {% if not param.name == "" %}{{ param.name }}{% else %}arg{{ param.index }}{% endif %}{%elif param.argumentLabel == param.name%}{{ param.name }}{%else%}{{ param.argumentLabel }} {{ param.name }}{% endif %}: {% if param.typeName.isClosure and param.typeName.closure.parameters.count > 1 %}({% endif %}{% call existentialParameterTypeName param.typeName param.isVariadic %}{% if not forloop.last %}, {% endif %}{% endfor -%}){% endmacro %}
The relevant part of AutoMockable.generated (reformatted for readability with a comment by me):
publicclassNetworkHandlingMock:NetworkHandling{publicinit(){}
//MARK: - handle<Request: NetworkRequest>
publicvarhandleRequestNetworkRequestRequestRequestRequestResponseDataThrowableError:(anyError)?publicvarhandleRequestNetworkRequestRequestRequestRequestResponseDataCallsCount=0publicvarhandleRequestNetworkRequestRequestRequestRequestResponseDataCalled:Bool{return handleRequestNetworkRequestRequestRequestRequestResponseDataCallsCount >0}publicvarhandleRequestNetworkRequestRequestRequestRequestResponseDataReceivedRequest:(Request)?publicvarhandleRequestNetworkRequestRequestRequestRequestResponseDataReceivedInvocations:[(Request)]=[]publicvarhandleRequestNetworkRequestRequestRequestRequestResponseDataReturnValue:Request.ResponseData!
// The next four declarations fail because `Request` is unknown
publicvarhandleRequestNetworkRequestRequestRequestRequestResponseDataClosure:((Request)asyncthrows->Request.ResponseData)?@discardableResultpublicfunc handle<Request:NetworkRequest>(request:Request)asyncthrows->Request.ResponseData{
handleRequestNetworkRequestRequestRequestRequestResponseDataCallsCount +=1
handleRequestNetworkRequestRequestRequestRequestResponseDataReceivedRequest = request
handleRequestNetworkRequestRequestRequestRequestResponseDataReceivedInvocations.append(request)
if let error = handleRequestNetworkRequestRequestRequestRequestResponseDataThrowableError {throw error
}
if let handleRequestNetworkRequestRequestRequestRequestResponseDataClosure = handleRequestNetworkRequestRequestRequestRequestResponseDataClosure {returntryawaithandleRequestNetworkRequestRequestRequestRequestResponseDataClosure(request)}else{return handleRequestNetworkRequestRequestRequestRequestResponseDataReturnValue
}}}
I guess the issue is that the types for the vars for the closure etcetera are typed towards the function, but the function itself is generic, so we don't know what kind of response a given request wants.
Manually I came to this solution:
classNetworkHandlingMock:NetworkHandling{privatevarpresetReturnValues:[String:Any]=[:]privatevarpresetErrors:[String:Error]=[:]private(set)varreceivedRequests:[Any]=[]@discardableResultfunc handle<Request:NetworkRequest>(request:Request)asyncthrows->Request.ResponseData{letkey=String(describing:Request.self)
receivedRequests.append(request)
if let error =presetErrors[key]{throw error
}
guard let returnValue =presetReturnValues[key]as?Request.ResponseDataelse{fatalError("No preset return value provided for \(Request.self)")}return returnValue
}func setReturnValue<Request:NetworkRequest>(_ returnValue:Request.ResponseData, for requestType:Request.Type){letkey=String(describing:Request.self)presetReturnValues[key]= returnValue
}func setError<Request:NetworkRequest>(_ error:Error, for requestType:Request.Type){letkey=String(describing:Request.self)presetErrors[key]= error
}}
I guess what I'm looking for is a way to at least erase the generic type to Any, and a bonus would be a way to say "if the Request is of Type X with ResponseType.Y, then the stored return value is ResponseType.Y". Is there any way to do this using a stencil for Sourcery?
Swift Version
swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
Target: arm64-apple-macosx14.0
Problem
I'm building a generic networking stack and it's working really well. However I want to mock the API and I am a bit stuck generating a Mock for it through Sourcery. I did some stencil modifications before, for example to generate @published / Publisher pairs automatically, so I'm not unfamiliar with this, but at the moment I'm stuck.
While it's not hard to mock the code below manually, I would like to understand the problem a bit better and I like things to work in a standardized way.
First my protocol and model:
The relevant part of my stencil:
The relevant part of
AutoMockable.generated
(reformatted for readability with a comment by me):I guess the issue is that the types for the vars for the closure etcetera are typed towards the function, but the function itself is generic, so we don't know what kind of response a given request wants.
Manually I came to this solution:
I guess what I'm looking for is a way to at least erase the generic type to
Any
, and a bonus would be a way to say "if the Request is of Type X with ResponseType.Y, then the stored return value is ResponseType.Y". Is there any way to do this using a stencil for Sourcery?Cross-posted on Stack Overflow as well. Mainly out of curiosity how alive SO still is: https://stackoverflow.com/questions/79248429/sourcery-stencil-for-generic-function-using-associatedtype
The text was updated successfully, but these errors were encountered: