• Stars
    star
    192
  • Rank 195,417 (Top 4 %)
  • Language
    Swift
  • License
    MIT License
  • Created 9 months ago
  • Updated about 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Magical testing tools for Swift macros.

Macro Testing

CI Slack

Magical testing tools for Swift macros.

An animated demonstration of macro tests being inlined.

Learn more

This library was designed to support libraries and episodes produced for Point-Free, a video series exploring the Swift programming language hosted by Brandon Williams and Stephen Celis.

You can watch all of the episodes here.

video poster image

Motivation

This library comes with a tool for testing macros that is more powerful and ergonomic than the default tool that comes with SwiftSyntax. To use the tool, simply specify the macros that you want to expand as well as a string of Swift source code that makes use of the macro.

For example, to test the #stringify macro that comes with SPM's macro template all one needs to do is write the following:

import MacroTesting
import XCTest

class StringifyTests: XCTestCase {
  func testStringify() {
    assertMacro(["stringify": StringifyMacro.self]) {
      """
      #stringify(a + b)
      """
    }
  }
}

When you run this test the library will automatically expand the macros in the source code string and write the expansion into the test file:

func testStringify() {
  assertMacro(["stringify": StringifyMacro.self]) {
    """
    #stringify(a + b)
    """
  } expansion: {
    """
    (a + b, "a + b")
    """
  }
}

That is all it takes.

If in the future the macro's output changes, such as adding labels to the tuple's arguments, then running the test again will produce a nicely formatted message:

❌ failed - Actual output (+) differed from expected output (βˆ’). Difference: …

- (a + b, "a + b")
+ (result: a + b, code: "a + b")

You can even have the library automatically re-record the macro expansion directly into your test file by providing the record argument to assertMacro:

assertMacro(["stringify": StringifyMacro.self], record: true) {
  """
  #stringify(a + b)
  """
} expansion: {
  """
  (a + b, "a + b")
  """
}

Now when you run the test again the freshest expanded macro will be written to the expansion trailing closure.

If you're writing many tests for a macro, you can avoid the repetitive work of specifying the macros in each assertion by using XCTest's invokeTest method to wrap each test with Macro Testing configuration:

class StringifyMacroTests: XCTestCase {
  override func invokeTest() {
    withMacroTesting(
      macros: ["stringify": StringifyMacro.self]
    ) {
      super.invokeTest()
    }
  }

  func testStringify() {
    assertMacro {  // πŸ‘ˆ No need to specify the macros being tested
      """
      #stringify(a + b)
      """
    } expansion: {
      """
      (a + b, "a + b")
      """
    }
  }

  // ...
}

You can pass the isRecording parameter to withMacroTesting to re-record every assertion in the test case (or suite, if you're using your own custom base test case class):

override func invokeTest() {
  withMacroTesting(
    isRecording: true
  ) {
    super.invokeTest()
  }
}

Macro Testing can also test diagnostics, such as warnings, errors, notes, and fix-its. When a macro expansion emits a diagnostic, it will render inline in the test. For example, a macro that adds completion handler functions to async functions may emit an error and fix-it when it is applied to a non-async function. The resulting macro test will fully capture this information, including where the diagnostics are emitted, how the fix-its are applied, and how the final macro expands:

func testNonAsyncFunctionDiagnostic() {
  assertMacro {
    """
    @AddCompletionHandler
    func f(a: Int, for b: String) -> String {
      return b
    }
    """
  } diagnostics: {
    """
    @AddCompletionHandler
    func f(a: Int, for b: String) -> String {
    ┬───
    ╰─ πŸ›‘ can only add a completion-handler variant to an 'async' function
       ✏️ add 'async'
      return b
    }
    """
  } fixes: {
    """
    @AddCompletionHandler
    func f(a: Int, for b: String) async -> String {
      return b
    }
    """
  } expansion: {
    """
    func f(a: Int, for b: String) async -> String {
      return b
    }

    func f(a: Int, for b: String, completionHandler: @escaping (String) -> Void) {
      Task {
        completionHandler(await f(a: a, for: b, value))
      }
    }
    """
  }
}

Documentation

The latest documentation for this library is available here.

License

This library is released under the MIT license. See LICENSE for details.

More Repositories

1

swift-composable-architecture

A library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind.
Swift
10,990
star
2

swift-snapshot-testing

πŸ“Έ Delightful Swift snapshot testing.
Swift
3,559
star
3

isowords

Open source game built in SwiftUI and the Composable Architecture.
Swift
2,515
star
4

swiftui-navigation

Tools for making SwiftUI navigation simpler, more ergonomic and more precise.
Swift
1,654
star
5

swift-dependencies

A dependency management library inspired by SwiftUI's "environment."
Swift
1,322
star
6

swift-tagged

🏷 A wrapper type for safer, expressive code.
Swift
1,289
star
7

swift-overture

🎼 A library for function composition.
Swift
1,115
star
8

pointfreeco

🎬 The source for www.pointfree.co, a video series on functional programming and the Swift programming language.
Swift
1,054
star
9

episode-code-samples

πŸ’Ύ Point-Free episode code.
Swift
922
star
10

swift-case-paths

🧰 Case paths extends the key path hierarchy to enum cases.
Swift
852
star
11

swift-nonempty

🎁 A compile-time guarantee that a collection contains a value.
Swift
817
star
12

swift-parsing

A library for turning nebulous data into well-structured data, with a focus on composition, performance, generality, and ergonomics.
Swift
802
star
13

swift-custom-dump

A collection of tools for debugging, diffing, and testing your application's data structures.
Swift
766
star
14

swift-html

πŸ—Ί A Swift DSL for type-safe, extensible, and transformable HTML documents.
Swift
726
star
15

combine-schedulers

⏰ A few schedulers that make working with Combine more testable and more versatile.
Swift
671
star
16

swift-web

πŸ•Έ A collection of Swift server-side frameworks for handling HTML, CSS, routing and middleware.
Swift
477
star
17

swift-identified-collections

A library of data structures for working with collections of identifiable elements in an ergonomic, performant way.
Swift
476
star
18

swift-prelude

🎢 A collection of types and functions that enhance the Swift language.
Swift
456
star
19

swift-perception

Observable tools, backported.
Swift
399
star
20

swift-validated

πŸ›‚ A result type that accumulates multiple errors.
Swift
389
star
21

swift-url-routing

A bidirectional router with more type safety and less fuss.
Swift
321
star
22

swift-concurrency-extras

Useful, testable Swift concurrency.
Swift
267
star
23

swift-gen

🎱 Composable, transformable, controllable randomness.
Swift
262
star
24

swift-clocks

⏰ A few clocks that make working with Swift concurrency more testable and more versatile.
Swift
229
star
25

swift-enum-properties

🀝 Struct and enum data access in harmony.
Swift
198
star
26

xctest-dynamic-overlay

Define XCTest assertion helpers directly in your application and library code.
Swift
197
star
27

syncups

A rebuild of Apple’s β€œScrumdinger” application using modern, best practices for SwiftUI development.
Swift
153
star
28

composable-core-location

A library that bridges the Composable Architecture and Core Location.
Swift
100
star
29

vapor-routing

A bidirectional Vapor router with more type safety and less fuss.
Swift
83
star
30

swift-html-vapor

πŸ’§ Vapor plugin for type-safe, transformable HTML views.
Swift
82
star
31

swift-playground-templates

🏫 A collection of helpful Xcode playground templates.
Makefile
80
star
32

pointfreeco-server

Point-Free server code.
39
star
33

swift-boundaries

🐣 Functional core, imperative shell.
Swift
27
star
34

composable-core-motion

A library that bridges the Composable Architecture and Core Motion.
Swift
26
star
35

swift-quickcheck

🏁 An implementation of QuickCheck in Swift.
Swift
24
star
36

swift-algebras

Algebraic laws bundled into concrete data types.
19
star
37

swift-either

For those times you want A or B!
Swift
19
star
38

swift-parser-printer

↔️ Parsing and printing
Swift
14
star
39

swift-html-kitura

☁️ Kitura plugin for type-safe, transformable HTML views.
Swift
13
star
40

homebrew-swift

Ruby
2
star
41

swift-bugs

2
star
42

Ccmark

Swift
1
star