Swift Testing #5: 인자를 사용한 파라미터화 테스트

발행: (2025년 12월 5일 오전 07:27 GMT+9)
7 min read
원문: Dev.to

Source: Dev.to

단일 조건 파라미터화

때때로 인자를 받는 특정 코드의 동작을 여러 값으로 검증해야 할 때가 있습니다. 전통적인 방법은 컬렉션을 만든 뒤 이를 순회하는 것입니다.

@Test("Even Value")
func even() {
  let values = [2, 8, 50]
  values.forEach { value in
    #expect(value.isMultiple(of: 2))
  }
}

위 구현도 동작하지만 몇 가지 단점이 있습니다:

  • 보고서에는 테스트 실행 결과가 전체적으로만 표시됩니다. 즉, 1000개의 시나리오를 실행했을 때 하나만 실패해도 전체 테스트가 실패한 것으로만 표시됩니다.
  • 특정 조건만 다시 실행할 수 없고, 모든 반복을 다 실행해야 합니다.

이 문제를 해결하기 위해 Swift Testing@Test는 컬렉션을 이용해 파라미터화할 수 있습니다. 이렇게 하면 컬렉션의 각 요소마다 테스트가 하나씩 실행됩니다. 이 접근법을 사용하면 테스트 보고서에 각 시나리오가 별도로 표시되고, 전체를 실행하지 않아도 특정 조건만 실행할 수 있습니다.

파라미터화된 테스트를 만들려면 테스트 메서드에 파라미터를 하나 추가하고, @Testarguments 인수에 값 컬렉션을 전달하면 됩니다( 문서 참고). 이러한 변경 덕분에 테스트 보고서에 각 조건이 독립적인 테스트처럼 구분되어 표시됩니다.

@Test("Even Value", arguments: [2, 8, 50])
func even(value: Int) {
  #expect(value.isMultiple(of: 2))
}

인자 조합 테스트

앞의 시나리오를 확장해서 두 컬렉션의 조건을 조합해 시스템을 테스트하고 싶다고 가정해 봅시다. 수동으로는 두 개의 반복문을 중첩해서 구현합니다.

@Test("Even Value")
func even() {
  let values1 = [2, 8, 50]
  let values2 = [3, 6, 9]
  values1.forEach { value1 in
    values2.forEach { value2 in
      let multiplication = value1 * value2
      #expect(multiplication.isMultiple(of: 2))
    }
  }
}

하지만 arguments가 두 개의 컬렉션을 받을 수 있는 오버로드를 활용하면 같은 결과를 더 간단히 얻을 수 있습니다( 문서 참고).

@Test("Even Value", arguments: [2, 8, 50], [3, 6, 9])
func even(first: Int, second: Int) {
  let multiplication = first * second
  #expect(multiplication.isMultiple(of: 2))
}

위 테스트 + 조건 조합에서는 총 9개의 시나리오가 생성됩니다. 그런데 모든 조합을 테스트하지 않고, 같은 인덱스에 해당하는 조합만 하나씩 테스트하고 싶다면 어떻게 할까요?

튜플로 매칭된 조건

컬렉션을 조합하지 않고 N개의 조건을 가진 단일 입력 컬렉션을 사용해야 합니다(N > 1). 각 조건은 M개의 값을 갖는 튜플로 표현되며, 테스트 메서드에서는 M개의 파라미터로 구조 분해됩니다.

@Test("Even Value", arguments: [(2, 3, true), (3, 5, false)])
func even(first: Int, second: Int, expectedResult: Bool) {
  let multiplication = first * second
  #expect(multiplication.isMultiple(of: 2) == expectedResult)
}

일대일 조합 특성 때문에 zip 연산자를 사용해 두 컬렉션으로부터 Zip2Sequence 타입의 컬렉션을 만들 수 있습니다. 이렇게 하면 컬렉션을 별도로 식별할 수 있어 테스트 가독성이 향상됩니다.

@Test("Even Value", arguments: [zip([2, 3], [3, 4])])
func even(first: Int, second: Int, expectedResult: Bool) {
  let multiplication = first * second
  #expect(multiplication.isMultiple(of: 2) == expectedResult)
}

하지만 @Test는 두 컬렉션만 결합할 수 있다는 제한이 있습니다( 문서 참고). 따라서 두 개 이상을 결합해야 할 경우, M개의 요소를 갖는 튜플을 원소로 하는 단일 컬렉션을 만드는 것이 가장 좋습니다(M > 2).

구조체로 매칭된 조건

두 개 이상의 컬렉션을 결합하는 문제를 해결하기 위해 튜플을 사용하는 것이 가장 간단하지만, 테스트 조건을 구성하는 값들을 속성으로 갖는 구조체를 사용해도 동일한 효과를 얻을 수 있습니다.

struct TestModel {
  let first: Int
  let second: Int
  let result: Int
}

@Test(arguments: [
  TestModel(first: 1, second: 2, result: 3),
  TestModel(first: 2, second: 3, result: 5),
  TestModel(first: 3, second: 4, result: 7),
])
func basic(testModel: TestModel) {
  let result = testModel.first + testModel.second
  #expect(result == testModel.result)
}

이 접근법은 테스트 구성 시 가독성을 높일 뿐만 아니라, 구조체가 CustomTestStringConvertible 프로토콜을 채택해 testDescription을 제공하도록 하면 보고서에 더 명확한 설명을 추가할 수 있습니다.

struct TestModel: CustomTestStringConvertible {
  let first: Int
  let second: Int
  let result: Int
  var testDescription: String {
    "\(first) + \(second) debería ser igual a \(result)"
  }
}

@Test(arguments: [
  TestModel(first: 1, second: 2, result: 3),
  TestModel(first: 2, second: 3, result: 5),
  TestModel(first: 3, second: 4, result: 7),
])
func basic(testModel: TestModel) {
  let result = testModel.first + testModel.second
  #expect(result == testModel.result)
}

이 경우 보고서에는 다음과 같이 읽기 쉬운 설명이 각각 표시됩니다:

1 + 2 debería ser igual a 3
2 + 3 debería ser igual a 5
3 + 4 debería ser igual a 7

참고 문헌

  • 영상 “Mastering Swift Testing: Eliminate Duplicate Tests with Parameterized Testing” (Swift and Tips), 여기.
  • Swift Testing 문서, 여기.
  • “Implementing parameterized tests” 문서, 여기.
  • 기사 “Swift Parameterized Testing”, 여기.
  • 기사 “Introducing Swift Testing. Parameterized Tests.”, 여기.
Back to Blog

관련 글

더 보기 »