如何用Swift Testing测试含不同类型元素矩阵的泛型函数?
Swift泛型矩阵中心点镜像函数的测试问题
背景情况
我有一个Swift泛型函数,可对任意元素类型的方阵进行中心点镜像:
func pointMirroredMatrix<T>(_ matrix: inout [[T]]) { assert(matrix.count > 0 && matrix[0].count > 0) // 确保矩阵非空 assert(matrix.count == matrix[0].count) // 确保矩阵是方阵 let n = matrix.count // 矩阵为n×n for row in 0 ..< n/2 { for col in 0 ..< n { // 交换元素 (matrix[row][col], matrix[n - 1 - row][n - 1 - col]) = (matrix[n - 1 - row][n - 1 - col], matrix[row][col]) } } }
我想要编写测试验证这个函数的正确性,但由于测试框架的@Test不支持泛型,所以定义了如下测试参数和测试结构:
typealias PointMirroredMatrixParams = (matrix: [[Any]], expectedResult: [[Any]]) let pointMirroredMatrixArgs: [PointMirroredMatrixParams] = [(matrix: [[0, 1], [0, 0]], expectedResult: [[0, 0], [1, 0]])] struct VisionTests { @Test("pointMirroredMatrix", arguments: pointMirroredMatrixArgs) func pointMirroredMatrix(p: PointMirroredMatrixParams) throws { // 测试逻辑见下文 } }
测试逻辑大致如下:
var mirroredMatrix = deepCopyMatrix(p.matrix) // 创建深拷贝 pointMirroredMatrix(&mirroredMatrix) // 对拷贝执行镜像操作 let mirroredCorrectly = maticesAreEqual(p.expectedResult, mirroredMatrix) #expect(mirroredCorrectly)
遇到的问题
我无法正确实现maticesAreEqual函数,因为测试参数的矩阵元素类型是Any。尝试了以下代码但编译失败:
func maticesAreEqual(_ matrix1: [[Any]], _ matrix2: [[Any]]) -> Bool { guard matrix1.count == matrix2.count else { return false } guard matrix1[0].count == matrix2[0].count else { return false } guard !matrix1.isEmpty else { return true } guard type(of: matrix1[0][0]) == type(of: matrix2[0][0]) else { return false } guard type(of: matrix1[0][0]) == (any Equatable).self else { return false } // 此时已知两个矩阵维度相同、元素类型一致且符合Equatable协议 // 但如何判断元素相等? for i in 0 ..< matrix1.count { for j in 0 ..< matrix1[i].count { if matrix1[i][j] != matrix2[i][j] { return false } // 编译错误 } } return true }
内层循环的代码无法编译,错误信息为:Type 'Any' cannot conform to 'Equatable'(注:原错误描述有误,实际是Any未遵循Equatable协议,无法直接用!=比较)。
我的问题
如何通过@Test传入不同元素类型的矩阵,来测试pointMirroredMatrix函数?
解决方案
方案1:基于类型擦除实现Equatable比较
通过将Any元素向下转换为any Equatable,并借助扩展方法实现类型安全的相等判断,修改maticesAreEqual函数如下:
func maticesAreEqual(_ matrix1: [[Any]], _ matrix2: [[Any]]) -> Bool { guard matrix1.count == matrix2.count else { return false } for (row1, row2) in zip(matrix1, matrix2) { guard row1.count == row2.count else { return false } for (elem1, elem2) in zip(row1, row2) { // 验证元素类型一致且遵循Equatable guard let equatable1 = elem1 as? any Equatable, let equatable2 = elem2 as? any Equatable, type(of: equatable1) == type(of: equatable2) else { return false } // 调用扩展方法执行相等判断 if !equatable1.isEqual(to: equatable2) { return false } } } return true } // 给Equatable扩展类型擦除后的比较方法 extension Equatable { func isEqual(to other: any Equatable) -> Bool { guard let otherTyped = other as? Self else { return false } return self == otherTyped } }
方案2:拆分测试用例,避免使用Any
针对不同元素类型(如Int、String、自定义Equatable类型)单独编写测试函数,绕过泛型参数限制:
struct VisionTests { @Test("pointMirroredMatrix_Int") func pointMirroredMatrixInt() throws { var matrix = [[0, 1], [0, 0]] let expected = [[0, 0], [1, 0]] pointMirroredMatrix(&matrix) #expect(matrix == expected) } @Test("pointMirroredMatrix_String") func pointMirroredMatrixString() throws { var matrix = [["a", "b"], ["c", "d"]] let expected = [["d", "c"], ["b", "a"]] pointMirroredMatrix(&matrix) #expect(matrix == expected) } }
方案3:用泛型包装器复用测试逻辑
创建泛型测试逻辑函数,在不同测试用例中调用,减少重复代码:
private func runPointMirroredMatrixTest<T: Equatable>(matrix: [[T]], expected: [[T]]) throws { var mirrored = matrix pointMirroredMatrix(&mirrored) #expect(mirrored == expected) } struct VisionTests { @Test("pointMirroredMatrix_Int") func pointMirroredMatrixInt() throws { try runPointMirroredMatrixTest(matrix: [[0, 1], [0, 0]], expected: [[0, 0], [1, 0]]) } @Test("pointMirroredMatrix_String") func pointMirroredMatrixString() throws { try runPointMirroredMatrixTest(matrix: [["a", "b"], ["c", "d"]], expected: [["d", "c"], ["b", "a"]]) } }
内容的提问来源于stack exchange,提问作者Reinhard Männer
相关产品推荐
相关产品推荐

