Swift Academy · Lesson

Testing and Debugging Macros

Writing unit tests for macro expansions using assertMacroExpansion.

Lesson 4 of 413 steps

Testing and Debugging Macros is a free Swift Academy lesson on CoddyKit. This is lesson 4 of 4. Any 3 lessons of this learning path are free to read in full — after that, CoddyKit PRO unlocks every lesson, plus hands-on practice with a built-in code editor and a 24/7 AI tutor. It is part of the Swift Academy learning path, and your progress syncs across the web and the CoddyKit app. The Swift Academy course includes 4 lessons in total.

Why Test Macros?

Macros generate code at compile time. Unit tests verify the generated source before it reaches production.

// The swift-syntax package ships SwiftSyntaxMacrosTestSupport
// with assertMacroExpansion for testing macro output

assertMacroExpansion

assertMacroExpansion takes input source and expected expanded source and verifies they match.

import SwiftSyntaxMacrosTestSupport
import XCTest

final class StringifyMacroTests: XCTestCase {
  func testStringify() throws {
    assertMacroExpansion(
      "#stringify(1 + 2)",
      expandedSource: "(1 + 2, \"1 + 2\")",
      macros: ["stringify": StringifyMacro.self]
    )
  }
}

Testing Attached Macros

Provide the decorated struct/class as input and the fully expanded source as the expected output.

assertMacroExpansion(
  "@AddID struct User { var name: String }",
  expandedSource: """
  struct User {
    var name: String
    var id = UUID()
  }
  """,
  macros: ["AddID": AddIDMacro.self]
)

Testing Diagnostic Emission

Verify that your macro emits the correct diagnostic (warning/error) when misused.

assertMacroExpansion(
  "@AddID class Foo {}",
  expandedSource: "class Foo {}",
  diagnostics: [
    DiagnosticSpec(message: "@AddID can only be applied to structs", line: 1, column: 1)
  ],
  macros: ["AddID": AddIDMacro.self]
)

Expanding Macros in Xcode

In Xcode, right-click a macro invocation and select Expand Macro to inspect the generated code inline.

// Before expansion:
let (v, s) = #stringify(1 + 2)
// After:
let (v, s) = (1 + 2, "1 + 2")

Debugging with print in Macros

Use context.diagnose or print to stderr to debug macro logic during development.

// In macro implementation:
print("[StringifyMacro] expanding: \(node.description)", to: &standardError)

Breakpoints in Macro Tests

Because macros run in-process during tests, you can set Xcode breakpoints inside macro implementations during test runs.

// Set a breakpoint in StringifyMacro.expansion()
// Run the test target — breakpoints are hit

Syntax Tree Inspection

Use SwiftSyntax's .debugDescription to print the full syntax tree of an input node for debugging.

print(node.debugDescription)
// Prints the full SyntaxTree hierarchy

Testing Multiple Argument Variants

Write one test per distinct argument shape to ensure all code paths in the macro are exercised.

func testNoArgument() {
  assertMacroExpansion(
    "#stringify()",
    expandedSource: "#stringify()",
    diagnostics: [DiagnosticSpec(message: "Need an argument", line: 1, column: 1)],
    macros: ["stringify": StringifyMacro.self]
  )
}

Integration Testing

Add a test app target that actually uses the macro. Confirm compilation and runtime behaviour.

// In test app:
let (val, src) = #stringify(Date.now)
XCTAssertEqual(src, "Date.now")

CI for Macro Tests

Run macro tests in CI like any other Swift package: swift test in the package directory.

// CI command:
swift test --package-path MyMacros

Quick Check

Which function verifies that a macro expands to the expected source code in a unit test?

Lesson Recap

Test macros with assertMacroExpansion for correct output and DiagnosticSpec for error cases. Debug with Xcode expansion preview and in-process breakpoints during test runs. Run via swift test in CI.

Free to start

Learn Swift with an AI tutor — free

Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.

Courses
122
Lessons
409

Frequently Asked Questions

Is the “Testing and Debugging Macros” lesson free?

Yes — any 3 lessons of the Swift Academy learning path, including “Testing and Debugging Macros”, are free to read in full here on the web. After that, CoddyKit PRO unlocks every lesson, plus interactive practice with a built-in code editor and a 24/7 AI tutor. The Swift Academy course includes 4 lessons in total.

What will I learn in “Testing and Debugging Macros”?

Writing unit tests for macro expansions using assertMacroExpansion. You practise Swift Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Swift Academy?

No prior experience is required. Swift Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 4 of 4.

How long does the “Testing and Debugging Macros” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Swift Academy lesson?

Yes. Every Swift Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Macro Roles: Freestanding vs Attached
  2. Writing a Freestanding Expression Macro
  3. Attached Member Macros for Boilerplate Reduction
  4. Testing and Debugging Macros
← Back to Swift Academy