Are these "tuple splat" parameterized tests legit?

Hi! I'm experimenting with parameterized testing in swift-testing. The documentation (and WWDC videos) I found highlight examples of:

  • Passing a single sequence (to a test that accepts one parameter).
  • Passing two sequences (to a test that accept two parameters).
  • Passing one sequence of 2D tuples (to a test that accepts one parameter).

I have tests that would prefer to test three parameters. The workaround I have so far is to pass a single sequences of 3D tuples:

var array: Array<(x: Int, y: Int, z: Int)> {
  var array = Array<(x: Int, y: Int, z: Int)>()
  array.append((1, 2, 3))
  array.append((4, 5, 6))
  array.append((7, 8, 9))
  return array
}

@Test(arguments: array) func t1(arguments : (x: Int, y: Int, z: Int)) async throws {
  #expect(arguments.x != .zero)
  #expect(arguments.y != .zero)
  #expect(arguments.z != .zero)
}

This test builds (and passes)… but I notice that I also have the option of parameterizing the test directly over the tuple elements:

var array: Array<(x: Int, y: Int, z: Int)> {
  var array = Array<(x: Int, y: Int, z: Int)>()
  array.append((1, 2, 3))
  array.append((4, 5, 6))
  array.append((7, 8, 9))
  return array
}

@Test(arguments: array) func t2(x: Int, y: Int, z: Int) async throws {
  #expect(x != .zero)
  #expect(y != .zero)
  #expect(z != .zero)
}

This is a nice shortcut… but I am wondering whether or not this is "legit" Swift. Here is another way of looking at my question:

var array: Array<(x: Int, y: Int, z: Int)> {
  var array = Array<(x: Int, y: Int, z: Int)>()
  array.append((1, 2, 3))
  array.append((4, 5, 6))
  array.append((7, 8, 9))
  return array
}

func f1(_: (x: Int, y: Int, z: Int)) {
  
}

func f2(x: Int, y: Int, z: Int) {
  
}

func main() {
  for element in array {
    f1(element)
    f2(element)
  }
}

This code fails to build:

11 | }
12 | 
13 | func f2(x: Int, y: Int, z: Int) {
   |      `- note: 'f2(x:y:z:)' declared here
14 |   
15 | }
   :
18 |   for element in array {
19 |     f1(element)
20 |     f2(element)
   |        `- error: global function 'f2' expects 3 separate arguments
21 |   }
22 | }

AFAIK this looks consistent with the removal of "tuple splat" behavior way back in SE-0029.

My question then is back to this example from swift-testing… is this (tuple splat) pattern actually a legit way to parameterize a Swift test? Is the fact that this seems to build and run (as of Xcode_16_beta_3) any sign that this might be a legit (supported) pattern for swift-testing going forward?

Any more ideas or advice about that? Thanks!

1 Like

It's fully supported and is, in fact, a language feature, not just a Swift Testing feature. We use it in our own tests.

1 Like

This is one of the use cases of forEach.

let array = [
  (1, 2, 3),
  (4, 5, 6),
  (7, 8, 9)
]
func f1(_: (x: Int, y: Int, z: Int)) { }
func f2(x: Int, y: Int, z: Int) { }
array.forEach(f1)
array.forEach(f2)

The first form, which takes a tuple, will yield a usage warning for mismatched labels. Tests do not care about labels.

@Test(
  arguments: [
    (a: 1, b: 2, c: 3),
    (a: 4, b: 5, c: 6),
    (a: 7, b: 8, c: 9)
  ]
) func t1(x: Int, y: Int, z: Int) { }

@Test(
  arguments: [
    (1, 2, 3),
    (4, 5, 6),
    (7, 8, 9)
  ]
) func t2(x: Int, y: Int, z: Int) { }
1 Like