Mind your argument names in Swift Testing’s parametrised tests!

As a follow up to my recent post on refactoring to use Swift Testing’s parametrised tests, I’m diving into the crucial - yet often overlooked topic of how to name your parametrised test inputs.

Option 1: First Named Tuples

Only the first tuple is named, all others rely on positional matching to (a, b, result).

  • ✅ Minimal boilerplate for small input sets
  • ❌ Readability drops after adding more cases
  • ❌ Easy to mix up arguments position
  • ❌ Hard to scan or extend
func add(_ a: Int, _ b: Int) -> Int {
    a + b
}

...

@Test(arguments: [
    (a: 1, b: 2, result: 3),
    (10, 15, 25),
    (1, -5, -4),
    (-1, -5, -6),
    (0, 0, 0),
    (1000, 1000, 2000),
    (10000, 50000, 60000),
    (-10, -3, -13)
])
func add_returnsCorrectSum(a: Int, b: Int, result: Int) {
    #expect(add(a, b) == result)
}

Option 2: Named Tuples

Every tuple entry explicitly names all its fields (a: …, b: …, result: …) for all cases.

  • ✅ Clear - no guessing which value is which
  • ✅ Easy to reorder or remove individual cases
  • ❌ More repetitive boilerplate per line
  • ❌ Becomes long and repetitive with many cases
@Test(arguments: [
    (a: 1, b: 2, result: 3),
    (a: 10, b: 15, result: 25),
    (a: 1, b: -5, result: -4),
    (a: -1, b: -5, result: -6),
    (a: 0, b: 0, result: 0),
    (a: 1000, b: 1000, result: 2000),
    (a: 10000, b: 50000, result: 60000),
    (a: -10, b: -3, result: -13)
])
func add_returnsCorrectSum(a: Int, b: Int, result: Int) {
    #expect(add(a, b) == result)
}

Option 3: Struct

Use a TestCase struct and pass it to @Test

  • ✅ Perfect separation of data vs testing logic
  • ✅ Great readability and maintainability
  • ✅ Unlimited scalability
  • ✅ IDE driven field suggestions
  • ❌ More upfront work (struct definition)
  • ❌ Might be overkill for 2-3 test cases
struct TestCase {
    let a: Int
    let b: Int
    let result: Int

    static var all: [TestCase] {
        [
            .init(a: 1, b: 2, result: 3),
            .init(a: 10, b: 15, result: 25),
            .init(a: 1, b: -5, result: -4),
            .init(a: -1, b: -5, result: -6),
            .init(a: 0, b: 0, result: 0),
            .init(a: 1000, b: 1000, result: 2000),
            .init(a: 10000, b: 50000, result: 60000),
            .init(a: -10, b: -3, result: -13)
        ]
    }
}

@Test(arguments: TestCase.all)
func add_returnsCorrectSum(testCase: TestCase) {
    #expect(add(testCase.a, testCase.b) == testCase.result)
}

Options Comparison

Criteria Option 1: First Named Tuple Option 2: Named Tuples Option 3: Struct
Readability Poor (at scale) High Highest
Scalability Poor Fair Excellent
Boilerplate Low Medium Medium (upfront)
Test arguments number 3 3 1

My final advice

No One-Size-Fits-All

No single strategy works for every test suite. Evaluate pros and cons of each strategy and choose the one that best fit your needs.

Remember - Tests are not just checks, they’re living documentation of your production code. Keep them super clear and don’t hesitate to ask your peers for feedback.

PDF version ⤵️ 3 Ways to Name Parameters in Swift Parametrised Tests


Thanks for reading. 📖

I hope you found it useful!

If you enjoy the topic don’t forget to follow me on one of my social media - LinkedIn, X, Mastodon, Bluesky or via RSS feed to keep up to speed. 🚀