Can't test macros on Xcode 15.1+

Hi! I try to use new macros on Xcode 15.1+. I created a default template from Apple (File -> New -> Package -> Macro...) and tried to start tests on Simulator (iOS 17.0.1) and tests throws error about "Test skipped - macros are only supported when running tests for the host platform"

Any ideas what's the problem?

But how I noticed on Xcode 15.0.1 all good.

Because macros run during compilation, they are executed on your Mac. You thus need to select your Mac as the run destination when testing macros.

2 Likes

You might want to check out swift-macro-testing by PointFree: GitHub - pointfreeco/swift-macro-testing: Magical testing tools for Swift macros.

I was able to solve this by creating a bridge module that expose the implementation as macro while the implementation itself is linked as normal target so it can be seen from unit tests.

targets: [
    .target(
        name: "TestSwiftMacros",
        dependencies: [
            "TestSwiftMacrosBridge"
        ]
    ),
    .macro( // Exposes the implementation as macros
        name: "TestSwiftMacrosBridge",
        dependencies: [
            "TestSwiftMacrosImplementation",
            .product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
            .product(name: "SwiftCompilerPlugin", package: "swift-syntax")
        ]
    ),
    .target( // The key is here, that the implementation is built as normal target
        name: "TestSwiftMacrosImplementation",
        dependencies: [
            .product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
            .product(name: "SwiftCompilerPlugin", package: "swift-syntax")
        ]
    ),
    .testTarget(
        name: "TestSwiftMacrosTests",
        dependencies: [
            "TestSwiftMacrosImplementation",
            .product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
        ]
    ),
]

Then in the bridge module, you will need to add a wrapper over the implementation

import TestSwiftMacrosImplementation
import SwiftCompilerPlugin
import SwiftSyntax
import SwiftSyntaxMacros

public struct TestSwiftMacroBridge: PeerMacro {
    public static func expansion(of node: AttributeSyntax,
                                 providingPeersOf declaration: some DeclSyntaxProtocol,
                                 in context: some MacroExpansionContext) throws -> [DeclSyntax] {
        try TestSwiftMacro.expansion(of: node, providingPeersOf: declaration, in: context)
    }
}

finally, update the main declaration to read the new bridge

@attached(member, names: arbitrary)
public macro TestSwift() = #externalMacro(module: "TestSwiftMacrosBridge", type: "TestSwiftMacroBridge")
2 Likes