如何编写包含两个以上参数的Swift参数化测试?
Swift Testing 实现多参数参数化测试的几种方案
针对Swift Testing中zip仅支持两个参数的限制,以下是三种实现多参数参数化测试的实用方案:
1. 直接使用多元素元组数组
@Test的arguments参数接受任意遵循Sequence协议的类型,因此可以手动构造包含多元素的元组数组,测试函数对应接收多个参数即可。
示例代码:
// 定义包含三个参数的测试用例数组 let multiParamTestCases: [(Food, Int, TimeInterval)] = [ (.burger, 50, 300), (.fries, 100, 200), (.shake, 30, 150), (.taco, 75, 250) ] @Test("Can handle large orders with wait time limits", arguments: multiParamTestCases) func testLargeOrderWithWaitTime(of food: Food, count: Int, maxWaitTime: TimeInterval) async throws { let foodTruck = FoodTruck(selling: food) let cookingResult = await foodTruck.cook(food, quantity: count, maxWaitTime: maxWaitTime) #expect(cookingResult) }
2. 用自定义结构体/枚举包装参数
当参数数量较多或需要明确每个参数的业务含义时,用自定义结构体包装参数会让测试用例更易读、易维护(结构体需遵循Sendable以适配异步测试)。
示例代码:
// 定义包装测试参数的结构体 struct OrderTestParameter: Sendable { let foodType: Food let orderCount: Int let allowedWaitTime: TimeInterval let shouldSucceed: Bool } // 构造结构化的测试用例数组 let structuredTestCases: [OrderTestParameter] = [ OrderTestParameter(foodType: .burger, orderCount: 50, allowedWaitTime: 300, shouldSucceed: true), OrderTestParameter(foodType: .fries, orderCount: 200, allowedWaitTime: 200, shouldSucceed: false), OrderTestParameter(foodType: .shake, orderCount: 30, allowedWaitTime: 150, shouldSucceed: true) ] @Test("Order success matches expected result", arguments: structuredTestCases) func testOrderSuccessAgainstParams(params: OrderTestParameter) async throws { let foodTruck = FoodTruck(selling: params.foodType) let result = await foodTruck.cook(params.foodType, quantity: params.orderCount, maxWaitTime: params.allowedWaitTime) #expect(result == params.shouldSucceed) }
3. 嵌套Zip或自定义多序列组合函数
如果需要将多个序列(如枚举、范围、数组)组合成测试参数,可以通过嵌套zip或者自定义组合函数实现。
嵌套Zip示例
let foodOptions = Food.allCases let orderCounts = 1...50 let waitTimeOptions: [TimeInterval] = [100, 200, 300] // 嵌套zip组合三个序列,最终序列长度取所有序列中最短的那个 @Test("Nested zip multi-parameter test", arguments: zip(zip(foodOptions, orderCounts), waitTimeOptions)) func testNestedZipParameters(foodCountPair: (Food, Int), waitTime: TimeInterval) async throws { let food = foodCountPair.0 let count = foodCountPair.1 let foodTruck = FoodTruck(selling: food) #expect(await foodTruck.cook(food, quantity: count, maxWaitTime: waitTime)) }
自定义多序列组合函数示例
如果觉得嵌套zip的元组结构不够直观,可以自定义支持多序列组合的函数:
// 自定义支持三个序列组合的序列类型 struct Zip3Sequence<A: Sequence, B: Sequence, C: Sequence>: Sequence { typealias Element = (A.Element, B.Element, C.Element) private let a: A private let b: B private let c: C init(_ a: A, _ b: B, _ c: C) { self.a = a self.b = b self.c = c } func makeIterator() -> Iterator { Iterator(a: a.makeIterator(), b: b.makeIterator(), c: c.makeIterator()) } struct Iterator: IteratorProtocol { var a: A.Iterator var b: B.Iterator var c: C.Iterator mutating func next() -> Element? { guard let aVal = a.next(), let bVal = b.next(), let cVal = c.next() else { return nil } return (aVal, bVal, cVal) } } } // 对外暴露简洁的调用函数 func zip3<A, B, C>(_ a: A, _ b: B, _ c: C) -> Zip3Sequence<A, B, C> where A: Sequence, B: Sequence, C: Sequence { Zip3Sequence(a, b, c) } // 使用自定义zip3组合三个序列 @Test("Custom zip3 multi-parameter test", arguments: zip3(Food.allCases, 1...50, [100,200,300])) func testCustomZip3Parameters(food: Food, count: Int, waitTime: TimeInterval) async throws { let foodTruck = FoodTruck(selling: food) #expect(await foodTruck.cook(food, quantity: count, maxWaitTime: waitTime)) }
内容的提问来源于stack exchange,提问作者Adil Hussain
相关产品推荐
相关产品推荐

