I am trying to create a macro to aid in testing on a production app and have been trying a fair few different things. More detail:
Desired Outcome:
In the project we have a lot of protocols, so given the below:
protocol Foo {
var bar: Bool { get }
func fooBar() -> Bool
}
We want to create a @Testable that would we would use like so:
@Testable
final class FooSpy: Foo { }
This class would be defined in the Testing bundle and we would expect the macro to expand into:
final class FooSpy: Foo {
var bar: Bool
var fooBarExpectation = XCTestExpectation()
var fooBarToReturn: Bool = false
func fooBar() -> Bool {
fooBarExpectation.fullFill()
return fooBarToReturn
}
}
Approach
I have managed to create a PeerMacro that does the above by setting the protocol as @Testable. The issue is that the macro expands in place where the protocol is and as such various testing related code doesn’t compile due to lack of imports etc.
I then went on to try and make an ExtensionMacro so that I could mark the Test class as @Testable which is inside the test target and thus has the required imports. The issue is, that with this approach I don’t seem to get access to the Protocols conformance requirements. Code below
public struct Testable: ExtensionMacro {
private static let extractor = Extractor()
public static func expansion(of node: SwiftSyntax.AttributeSyntax,
attachedTo declaration: some SwiftSyntax.DeclGroupSyntax,
providingExtensionsOf type: some SwiftSyntax.TypeSyntaxProtocol,
conformingTo protocols: [SwiftSyntax.TypeSyntax],
in context: some SwiftSyntaxMacros.MacroExpansionContext) throws -> [SwiftSyntax.ExtensionDeclSyntax] {
// Here I'm trying to get the protocol details (extractor below):
let protocolDeclaration = try extractor.extractProtocolDeclaration(from: declaration)
Extractor:
struct Extractor {
func extractClassDeclaration(
from declaration: DeclGroupSyntax
) throws -> ProtocolDeclSyntax {
guard let protocolDeclaration = declaration.as(ProtocolDeclSyntax) else {
throw TestableMacroError.invalidProtocol
}
return protocolDeclaration
}
}
The obvious issue is that with the ExtensionMacro, the declaration is no longer the same as when it’s a PeerMacro so this code doesn’t work.
How can I (if i even can) get the protocol details from a DeclGroupSyntax?




