---
title: "Swift Macro Expansion Testing"
source: "/writing/swift-macro-expansion-testing/"
---

Article

# Swift Macro Expansion Testing

Chris Sessions

August 13, 2025

This is the second article in the _Swift Macro Testing_ series. The purpose of the series is to explore a useful triad of testing approaches we use at Livefront when building Swift macros.

Over the course of this series, we'll follow a test-driven approach to develop a custom Swift macro from scratch. Before we dive in, let's make sure that everyone understands what macros are.

### About Swift Macros

Macros are a form of [metaprogramming](https://en.wikipedia.org/wiki/Metaprogramming) , allowing code to generate other code. Swift macros excel at making code more expressive and removing boilerplate.

Here are some fundamentals to understand about Swift macros:

-   Swift macros are evaluated at compile time, unlike normal code that is compiled and then evaluated at runtime.
-   The code that a Swift macro generates is called its _expansion._
-   Swift macro expansions are compiled alongside the normal source code, and the combined result is evaluated at runtime.
-   Swift macros are _additive_, meaning they add code to your program without modifying existing code directly.
-   Swift macros have access to a tree representation of any Swift source code they're attached to, and can also take parameters.

Swift macros come in two flavors, each with several types:

### Freestanding Macros

1.  **#expression macros** generate code that can be used as an expression, replacing the macro invocation with the generated code.
2.  **#declaration macros** create new declarations, such as functions or types, at the location where the macro is used.

### Attached Macros

1.  **＠member macros** add new members to the declaration they're attached to, such as properties or methods.
2.  **＠memberAttribute macros** apply attributes to existing members of the declaration to which they're attached.
3.  **＠extension macros** add new conformances or nested types to the declaration to which they're attached.
4.  **＠peer macros** generate new declarations adjacent to the one they're attached to, creating "peer" declarations.
5.  **＠accessor macros** generate custom accessor methods for properties.

### History

Swift macros arrived in Swift 5.9, and [despite](https://forums.swift.org/t/compilation-extremely-slow-since-macros-adoption/67921) [some](https://forums.swift.org/t/macro-adoption-concerns-around-swiftsyntax/66588) [ongoing](https://forums.swift.org/t/swift-macros-build-time-overhead-concerns/70443) [challenges](https://forums.swift.org/t/macros-and-xcframeworks/68122/34) , they've seen widespread adoption. Apple uses native macros extensively in [SwiftUI](https://developer.apple.com/xcode/swiftui/) and [Swift Testing](https://github.com/swiftlang/swift-testing) , and third-party libraries like Pointfree's [Composable Architecture](https://github.com/pointfreeco/swift-composable-architecture/tree/main) have created and leveraged custom macros to streamline their APIs.

### Introducing the ＠Memoized Macro

This article explores _expansion_ testing, the first of a useful triad of testing approaches that we use at Livefront when developing Swift macros. Expansion testing simply means verifying that a macro's expansion matches an expected string literal.

Exploring expansion testing requires a test subject, so we need an idea for a Swift macro. As luck would have it, we already came up with one in the first article in this series, " [Exploring Memoization in Swift](/writing/exploring-memoization-in-swift) ." In that article's conclusion, we imagined a Memoized macro:

struct Sample {
    @Memoized
    func fibonacci(_ n: Int) -> Int {
        if n < 0 {
            0
        } else if n < 2 {
            n
        } else {
            memoizedFibonacci(n - 1) + memoizedFibonacci(n - 2)
        }
    }
}

Sample().fibonacci(42) // 267,914,296

The Memoized macro's job is to _memoize_ a pure function to which it's attached. Anyone wishing to know more about this optimization technique should read " [Exploring Memoization in Swift](/writing/exploring-memoization-in-swift) ." In that article, we began with a prohibitively slow recursive Fibonacci function and developed a memoization solution that radically improved its performance.

In this article, we'll use that memoization solution as the basis for a new Memoized macro. Following a test-driven development approach, we'll begin by using the solution's source code as the expected output in a test. From there, we'll build the macro to fulfill that expectation.

### Preparing the Macro Package

Let's create a macro package. We'll start with Xcode's Swift Macro template, following these steps:

1. In Xcode's menu bar, select `File > New > Package...`

2. In the next window, select `Multiplatform > Swift Macro > Next`

3. Select the testing framework and click `Next`

4. Save the macro package as `Memoized`

### What's in the Package

-   **Memoized**: _A library that exposes a macro as part of its API, which is used in client programs._
-   **MemoizedClient**: _A client of the library, which is able to use the macro in its own code._
-   **MemoizedMacros**: _The macro implementation that performs the source transformation._
-   **MemoizedTests**: _A test target used to develop the macro implementation._

The macro template provides a sensible structure to get started. The targets contain a sample macro implementation -- Apple's `#stringify` example -- which we'll replace with our own code.

Starting with the **Memoized** target, replace the `#stringify` declaration with a `Memoized` macro declaration. This will be an attached peer macro, meaning it will generate new declarations alongside the function that we memoize.

// Sources/Memoized/Memoized.swift

@attached(peer, names: arbitrary)
public macro Memoized() = #externalMacro(
    module: "MemoizedMacros",
    type: "MemoizedMacro"
)

Moving on to the **MemoizedClient** target, we can remove everything but the import statement for now:

// Sources/MemoizedClient/main.swift

import Memoized

In the **MemoizedMacros** target, replace Apple's `StringifyMacro` sample with a bare `MemoizedMacro` definition. Each macro type has its own `expansion` function, and below is the one necessary for `PeerMacro` conformance. For now we'll return an empty array, which results in an empty expansion.

// Sources/MemoizedMacros/MemoizedMacro.swift

import SwiftDiagnostics
import SwiftSyntax
import SwiftSyntaxMacros

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

The template places the compiler plugin in the same file, but for better organization, let's move `MemoizedPlugin` into its own file:

// Sources/MemoizedMacros/MemoizedPlugin.swift

import SwiftCompilerPlugin
import SwiftSyntaxMacros

@main
struct MemoizedPlugin: CompilerPlugin {
    let providingMacros: [Macro.Type] = [
        MemoizedMacro.self,
    ]
}

Now we can turn to the **MemoizedTests** target, where we'll articulate our vision for the Memoized macro.

### `Expansion Testing: assertMacroExpansion`

In the **MemoizedTests** target, let's clear out the sample tests and add an initial expansion test using ⁠`assertMacroExpansion`. This is one of the nice things about developing Swift macros: they lend themselves well to a test-driven approach, and ⁠`assertMacroExpansion` provides a helpful starting point.

While we're at it, you may have noticed a lack of testing framework choices when using Xcode's Swift Macro template. At the time of writing, XCTest is the only option it provides. But in an article about macros and testing, the [Swift Testing](https://github.com/swiftlang/swift-testing) framework is a more appropriate choice:

// Tests/MemoizedTests/MemoizedTests.swift

import MemoizedMacros
import SwiftSyntaxMacros
import Testing

let testMacros: [String: Macro.Type] = [
    "Memoized": MemoizedMacro.self,
]

struct MemoizedTests {
    @Test("Memoized Fibonacci function")
    func memoizedFibonacciFunction() {
        assertMacroExpansion(
            "@Memoized private func _fibonacci(_ n: Int) -> Int {...}",
            expandedSource: "...",
            macros: testMacros
        )
    }
}

Unfortunately, the switch to Swift Testing results in a `🛑 ⁠Cannot find 'assertMacroExpansion' in scope` error, since this test helper only works with XCTest at the time of writing. While we could revert to XCTest, let's explore a better option.

### `Expansion Testing:` assertMacro

The `assertMacro` tool in Pointfree's [Macro Testing](https://github.com/pointfreeco/swift-macro-testing) library makes expansion testing incredibly easy, and it works with Swift Testing. It's a substantial quality-of-life improvement that simplifies the test code, so we'll use it here.

For those sticking with XCTest and ⁠`assertMacroExpansion`, everything works similarly but with more boilerplate. For everyone else, here are the package updates to install the Macro Testing library:

let package = Package(
    name: "Memoized",
    // ...
    dependencies: [
        .package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.6.0"),
        // ...
    ],
    targets: [
        // ...
        .testTarget(
            name: "MemoizedTests",
            dependencies: [
                // ...
                .product(name: "MacroTesting", package: "swift-macro-testing"),
            ]
        ),
        // ...
    ]
)

Now we need the following replacements in `MemoizedTests.swift`:

1.  Replace the `import SwiftSyntaxMacros` with `import MacroTesting`.
2.  Remove the `testMacros` declaration and add a `Suite` attribute on `MemoizedTests`. The suite contains a trait declaring the macro(s) we intend to test.
3.  Replace `assertMacroExpansion` with `assertMacro`.

Finally, copy the `fibonacci` function declaration that we imagined above and drop it into the `assertMacro` closure as a string.

// Tests/MemoizedTests/MemoizedTests.swift

import MacroTesting
import MemoizedMacros
import Testing

@Suite(.macros(
    macros: ["Memoized": MemoizedMacro.self]
))
struct MemoizedTests {
    @Test("Memoized Fibonacci function")
    func memoizedFibonacciFunction() {
        assertMacro {
            #"""
            @Memoized
            func fibonacci(_ n: Int) -> Int {
                if n < 0 {
                    0
                } else if n < 2 {
                    n
                } else {
                    memoizedFibonacci(n - 1) + memoizedFibonacci(n - 2)
                }
            }
            """#
        }
    }
}

The test fails when you run it for the first time, but you'll see an `expansion` closure appear in the code automatically. The closure contains a string literal with the current macro expansion. Run the test again and it passes:

// Tests/MemoizedTests/MemoizedTests.swift

@Test("Memoized Fibonacci function")
func memoizedFibonacciFunction() {
    assertMacro {
        #"""
        @Memoized
        func fibonacci(_ n: Int) -> Int {
            if n < 0 {
                0
            } else if n < 2 {
                n
            } else {
                memoizedFibonacci(n - 1) + memoizedFibonacci(n - 2)
            }
        }
        """#
    } expansion: {
        """
        func fibonacci(_ n: Int) -> Int {
            if n < 0 {
                0
            } else if n < 2 {
                n
            } else {
                memoizedFibonacci(n - 1) + memoizedFibonacci(n - 2)
            }
        }
        """
    }
}

### Defining the Macro Expansion

So far the expansion doesn't include any additional code because we haven't given the macro anything to do yet. Before implementing the functionality, let's specify what we want the macro expansion to look like when we're finished.

In the " [Exploring Memoization in Swift](/writing/exploring-memoization-in-swift) " article, we wrote a memoization implementation for the fibonacci function. Let's grab the final source code from that article and drop it into the test's expansion closure:

@Test("Memoized Fibonacci function")
func memoizedFibonacciFunction() {
    assertMacro {
        #"""
        @Memoized
        func fibonacci(_ n: Int) -> Int {
            if n < 0 {
                0
            } else if n < 2 {
                n
            } else {
                memoizedFibonacci(n - 1) + memoizedFibonacci(n - 2)
            }
        }
        """#
    } expansion: {
        """
        func fibonacci(_ n: Int) -> Int {
            if n < 0 {
                0
            } else if n < 2 {
                n
            } else {
                memoizedFibonacci(n - 1) + memoizedFibonacci(n - 2)
            }
        }

        private var _fibonacciCache = [Int: Int]()

        func memoizedFibonacci(_ n: Int) -> Int {
            if let cached = _fibonacciCache[n] {
                return cached
            }
            let result = fibonacci(n)
            _fibonacciCache[n] = result
            return result
        }
        """
    }
}

This is the essence of expansion testing: the first closure contains an input, the `expansion` closure contains the expected output. Running the test again outputs the difference between the actual and expected output, telling us what we need to implement:

Suite MemoizedTests started.
Test "Memoized Fibonacci function" started.
Test "Memoized Fibonacci function" recorded an issue at MemoizedTests.swift:24:21: Issue recorded
Expanded output (+) differed from expected output (−). Difference: ...
    func fibonacci(_ n: Int) -> Int {
        if n < 0 {
            0
        } else if n < 2 {
            n
        } else {
            memoizedFibonacci(n - 1) + memoizedFibonacci(n - 2)
        }
    }
  -
  - private var _fibonacciCache = [Int: Int]()
  -
  - func memoizedFibonacci(_ n: Int) -> Int {
  -     if let cached = _fibonacciCache[n] {
  -         return cached
  -     }
  -     let result = fibonacci(n)
  -     _fibonacciCache[n] = result
  -     return result
  - }
Test "Memoized Fibonacci function" failed after 0.386 seconds with 1 issue.

### Defining the Cache Variable

Let's return to the **MemoizedMacros** target and start implementing the missing expansion elements. We'll begin with the ⁠`_fibonacciCache` variable declaration.

The test expects a variable called ⁠`_fibonacciCache`, but this risks causing name collisions. Fortunately, ⁠`MacroExpansionContext` provides a ⁠`makeUniqueName` function that plays nicely with tests, so let's use that.

We'll create a string literal representation of the source code we want added in the macro expansion, and then return that as a ⁠`DeclSyntax` node:

public struct MemoizedMacro: PeerMacro {
    public static func expansion(
        of node: AttributeSyntax,
        providingPeersOf declaration: some DeclSyntaxProtocol,
        in context: some MacroExpansionContext
    ) throws -> [DeclSyntax] {
        let cacheVarName = context.makeUniqueName("Cache")
        let memoCacheDeclaration = "private var \(cacheVarName) = [Int: Int]()"

        return [
            DeclSyntax(stringLiteral: memoCacheDeclaration),
        ]
    }
}

Running tests again after these changes reveals that `⁠context.makeUniqueName("Cache")` resolved to `⁠__macro_local_5CachefMu_`. This is a guaranteed unique name, and importantly, not a random name that would make our tests brittle:

@Test("Memoized Fibonacci function")
func memoizedFibonacciFunction() {
    assertMacro {
        // ...
    } expansion: {
        """
        func fibonacci(_ n: Int) -> Int {
            if n < 0 {
                0
            } else if n < 2 {
                n
            } else {
                memoizedFibonacci(n - 1) + memoizedFibonacci(n - 2)
            }
        }

        private var __macro_local_5CachefMu_ = [Int: Int]()

        func memoizedFibonacci(_ n: Int) -> Int {
            if let cached = __macro_local_5CachefMu_[n] {
                return cached
            }
            let result = fibonacci(n)
            __macro_local_5CachefMu_[n] = result
            return result
        }
        """
    }
}

Running the tests again shows our progress:

Expanded output (+) differed from expected output (−). Difference: ...
  func fibonacci(_ n: Int) -> Int {
      if n < 0 {
          0
      } else if n < 2 {
          n
      } else {
          memoizedFibonacci(n - 1) + memoizedFibonacci(n - 2)
      }
  }

  private var __macro_local_5CachefMu_ = [Int: Int]()
−
− func memoizedFibonacci(_ n: Int) -> Int {
−     if let cached = __macro_local_5CachefMu_[n] {
−         return cached
−     }
−     let result = fibonacci(n)
−     __macro_local_5CachefMu_[n] = result
−     return result
− }

### A Word about Swift Syntax

Before continuing, a quick note on [_Swift_](https://github.com/swiftlang/swift-syntax) [_Syntax_](https://swiftpackageindex.com/swiftlang/swift-syntax/600.0.1/documentation/swiftsyntax) might be helpful. Working with Swift Syntax in depth is beyond the scope of this article, but it's what macro expansion functions use to analyze and generate code. According to its documentation: "The swift-syntax package is a set of libraries that work on a source-accurate tree representation of Swift source code, called the SwiftSyntax tree."

At the time of writing, the documentation is somewhat sparse, so you'll need to rely on IDE features like Jump to Definition, tools like the [Swift AST Explorer](https://swift-ast-explorer.com/) , and AI assistants to navigate its complexities.

### Defining the `Memoized Function`

Moving on to the ⁠`memoizedFibonacci` definition, let's examine what we need. Generating `func memoizedFibonacci(_ n: Int) -> Int` will require the ⁠function name, ⁠parameters, and ⁠return type of the original ⁠`fibonacci` function. Once we have these, we'll create something like:

func memoized\(functionName)(\(parameters)) -> \(returnType) {...}

The `expansion` function receives a SwiftSyntax tree representation of the element to which we attached the macro -- in this case, the ⁠`fibonacci` function. It arrives in the ⁠`declaration` parameter, so that's where we'll look for the elements we need.

Here's our initial approach to generating the memoized function declaration:

public static func expansion(
    of node: AttributeSyntax,
    providingPeersOf declaration: some DeclSyntaxProtocol,
    in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
    guard let functionDeclaration = declaration.as(FunctionDeclSyntax.self) else {
        fatalError("Throw some error: functionDeclaration failed")
    }
    guard let functionReturnClause = functionDeclaration.signature.returnClause else {
        fatalError("Throw some error: functionReturnClause failed")
    }

    let cacheVarName = context.makeUniqueName("Cache")
    let functionName = functionDeclaration.name.text
    let parameters = functionDeclaration.signature.parameterClause.parameters
    let returnType = functionReturnClause.type.trimmed.description

    let memoCacheDeclaration = "private var \(cacheVarName) = [Int: Int]()"

    let memoFunctionDeclaration = """
        func memoized\(functionName)(\(parameters)) -> \(returnType) {
            if let cached = \(cacheVarName).storage[key] {
                return cached
            }
            let result = \(functionName)(\(parameters)))
            \(cacheVarName).storage[key] = result
            return result
        }
        """

    return [
        DeclSyntax(stringLiteral: memoCacheDeclaration),
        DeclSyntax(stringLiteral: memoFunctionDeclaration),
    ]
}

The first ⁠`guard` statement attempts to cast ⁠`declaration` as a ⁠`FunctionDeclSyntax` element. The SwiftSyntax tree representation is flexible like the language itself, and it's common to receive a node with only a general type. Before we can work with such a node effectively, we need to cast it to a more specific type.

Once we have a strongly typed ⁠`functionDeclaration`, finding everything else is relatively straightforward. In most cases, we can use Xcode's code completion to navigate to the appropriate nodes.

Running tests again reveals significant progress:

Test "Memoized Fibonacci function" recorded an issue at MemoizedTests.swift:24:21: Issue recorded
Expanded output (+) differed from expected output (−). Difference: ...
  func fibonacci(_ n: Int) -> Int {
      if n < 0 {
          0
      } else if n < 2 {
          n
      } else {
          memoizedFibonacci(n - 1) + memoizedFibonacci(n - 2)
      }
  }

  private var __macro_local_5CachefMu_ = [Int: Int]()

− func memoizedFibonacci(_ n: Int) -> Int {
+ func memoizedfibonacci(_ n: Int) -> Int {
      if let cached = __macro_local_5CachefMu_[n] {
          return cached
      }
−     let result = fibonacci(n)
+     let result = fibonacci(_ n: Int))
      __macro_local_5CachefMu_[n] = result
      return result
  }
Test "Memoized Fibonacci function" failed after 0.410 seconds with 1 issue.

There are two issues we need to fix:

1.  We need to properly camel-case the memoized function name.
2.  When calling the ⁠`fibonacci` function, we're using the function signature parameters instead of function invocation arguments. While this was correct in the ⁠`memoizedFibonacci` declaration, we need to translate these into the appropriate format for the function call.

We'll address these issues by creating two helper functions:

1.  ⁠`memoFunctionName(for:)` to compute the camel-cased memoized function name.
2.  ⁠`invocationArguments(for:)` to translate parameters into invocation arguments.

The resulting ⁠memoFunctionDeclaration would look like this:

let memoFunctionName = memoFunctionName(for: functionName)
let invocationArguments = invocationArguments(for: parameters)

let memoFunctionDeclaration = """
    func \(memoFunctionName)(\(parameters)) -> \(returnType) {
        let key = "\(keyExpression(for: parameters))"
        if let cached = \(cacheVarName).storage[key] {
            return cached
        }
        let result = \(functionName)(\(invocationArguments))
        \(cacheVarName).storage[key] = result
        return result
    }
    """

The ⁠`memoFunctionName(for:)` function should be straightforward, but the ⁠`invocationArguments(for:)` function will need to handle various edge cases. We'll want comprehensive unit tests to ensure it handles all situations correctly.

We'll explore these functions and their unit tests in " [Swift Macro Unit Testing](https://medium.com/@chris_91497/swift-macro-unit-testing-eb71ccecccda) ," the next article in this series. But before we conclude, let's look at another important feature that expansion testing offers.

### Macro Diagnostics

So far, we've focused on the happy path: applying the Memoized macro to a pure function. Now let's consider situations where things might go wrong. What if, for example, a developer tries to memoize a struct?

@Memoized struct Foo {} // Memoized only applies to pure functions!

Since macros are evaluated at _compile_ time, runtime responses like alerts aren't applicable. Instead, we need to consider how to present errors at compile time in Xcode.

Compiler errors appear inline in the source code, highlighting the problematic line with an error message describing what went wrong. You can trigger such an error from your macro by throwing a ⁠`DiagnosticsError`.

Let's replace the ⁠`fatalError` placeholders in our expansion function's guard statements with proper diagnostic errors:

// Sources/MemoizedMacros/MemoizedMacro.swift

public struct MemoizedMacro: PeerMacro {
    public static func expansion(
        of node: AttributeSyntax,
        providingPeersOf declaration: some DeclSyntaxProtocol,
        in context: some MacroExpansionContext
    ) throws -> [DeclSyntax] {
        guard let functionDeclaration = declaration.as(FunctionDeclSyntax.self) else {
            throw functionsOnlyError(on: node)
        }
        guard let functionReturnClause = functionDeclaration.signature.returnClause else {
            throw requiresReturnTypeError(on: functionDeclaration.signature)
        }
        // ...
    }
}

extension MemoizedMacro {
    static func functionsOnlyError(on node: AttributeSyntax) -> DiagnosticsError {
        DiagnosticsError(
            diagnostics: [
                Diagnostic(
                    node: Syntax(node),
                    message: MemoizedError.functionsOnly
                )
            ]
        )
    }

    static func requiresReturnTypeError(on signature: FunctionSignatureSyntax) -> DiagnosticsError {
        DiagnosticsError(
            diagnostics: [
                Diagnostic(
                    node: Syntax(signature),
                    message: MemoizedError.requiresReturnType
                )
            ]
        )
    }
}

enum MemoizedError: DiagnosticMessage {
    case functionsOnly
    case requiresReturnType

    var message: String {
        switch self {
        case .functionsOnly: "Memoized can only be applied to functions"
        case .requiresReturnType: "Memoized requires a declared return type"
        }
    }

    var diagnosticID: SwiftDiagnostics.MessageID {
        .init(domain: "MemoizedMacro", id: "\(self)")
    }

    var severity: SwiftDiagnostics.DiagnosticSeverity {
        .error
    }
}

Now let's test out our first `DiagnosticsError`:

// Tests/MemoizedTests/MemoizedTests.swift

@Test("Memoized Foo struct")
func memoizedFooStruct() {
    assertMacro {
        "@Memoized struct Foo {}"
    }
}

When we run this test, instead of seeing an `expansion` closure as before, we get a `diagnostics` closure showing how an inline error will appear in Xcode:

// Tests/MemoizedTests/MemoizedTests.swift

@Test("Memoized Foo struct")
func memoizedFooStruct() {
    assertMacro {
        "@Memoized struct Foo {}"
    } diagnostics: {
        """
        @Memoized struct Foo {}
        ┬────────
        ╰─ 🛑 Memoized can only be applied to functions
        """
    }
}

### Fix-it Diagnostics

Swift macros can also emit "fix-it" diagnostics that suggest corrections to the source code. A common example of this is when writing a `switch` statement, where Xcode offers to add the missing enum cases.

Swift macros can emit fix-it diagnostics like these

While there isn't a helpful fix to suggest for applying the Memoized macro to a struct, we've at least provided a clear error message explaining the issue.

Here is the actual error in Xcode

### Swift Macro Unit Testing

This concludes our exploration of Swift Macro Expansion Testing, where we've examined Swift macros from the ground up and followed a test-driven approach to develop a custom Memoized macro.

We've focused on expansion testing, which verifies the string output generated by our macro. We've made significant progress, but we've reached a point where it makes sense to add more granular unit tests for the individual components of our macro.

The _Swift Macro Testing_ series continues in " [Swift Macro Unit Testing](https://medium.com/@chris_91497/swift-macro-unit-testing-eb71ccecccda) ," where we'll refine our Memoized macro. We'll continue following a test-driven approach while focusing on unit testing specific helper functions. This will demonstrate how unit tests complement the expansion tests we've explored in this article, providing a comprehensive testing strategy for macro development.

-   Chris Sessions

    Software Engineer

    [More from Chris](/team/chris-sessions/)

    Chris Sessions

### Tags

[Digital Engineering](/insights/all/?topic=digital-engineering) [Mobile Apps](/insights/all/?topic=mobile-apps)

### Share

-   Copied

## View these next.

[See all](/insights/all/?type=writing)

-   [

    Article

    How To Sabotage Your Project Using Inconsistency

    Collin Flynn

    ](/writing/how-to-sabotage-your-project-using-inconsistency/)
-   [

    Article

    Unit Testing race conditions by creating chaos (Swift)

    Sean Berry

    ](/writing/unit-testing-race-conditions-by-creating-chaos-swift/)
-   [

    Article

    UIApplicationDelegate call sequence reference

    Sean Berry

    ](/writing/uiapplicationdelegate-call-sequence-reference/)
