Swift中如何对返回函数的函数进行单元测试?
Swift返回函数类型的单元测试方案
Swift没有提供获取函数文本定义的原生能力,直接打印函数实例只会得到固定的(Function)标识,无法通过类似其他语言toString的方式做校验。这类场景的核心测试思路是校验返回函数的执行行为是否符合预期,而非校验函数本身的内存标识或文本定义,以下是不同场景下的具体实现方案:
方案1:针对产生输出/副作用的返回函数
如果返回的函数执行后会产生可观测的输出(比如控制台打印、网络请求、文件写入等),可以通过捕获对应输出的方式校验结果。
以你示例中的打印场景为例,先实现标准输出捕获工具,再执行返回的闭包校验打印内容:
/// 捕获代码块执行的控制台标准输出 func captureStandardOutput(_ block: () -> Void) -> String { let originalStdout = dup(STDOUT_FILENO) let pipe = Pipe() dup2(pipe.fileHandleForWriting.fileDescriptor, STDOUT_FILENO) block() dup2(originalStdout, STDOUT_FILENO) close(originalStdout) pipe.fileHandleForWriting.closeFile() let outputData = pipe.fileHandleForReading.readDataToEndOfFile() return String(data: outputData, encoding: .utf8)?.trimmingCharacters(in: .newlines) ?? "" }
对应的单元测试用例:
import XCTest final class FunctionReturnTests: XCTestCase { func testFlagTrueReturnedFunction() { let returnedFunc = someFunction(flag: true) let output = captureStandardOutput { returnedFunc() } XCTAssertEqual(output, "True") } func testFlagFalseReturnedFunction() { let returnedFunc = someFunction(flag: false) let output = captureStandardOutput { returnedFunc() } XCTAssertEqual(output, "False") } }
方案2:针对修改状态的返回函数
如果返回的闭包作用是修改指定对象的状态,直接构造测试用的状态实例,调用闭包后校验状态变更结果即可。
示例场景:
struct User { var name: String } func makeUserUpdateFunc(useDefaultName: Bool) -> (inout User) -> Void { if useDefaultName { return { $0.name = "默认名称" } } else { return { $0.name = "自定义名称" } } }
对应测试用例:
func testUserUpdateFunc() { var testUser = User(name: "初始名称") let updateFunc = makeUserUpdateFunc(useDefaultName: true) updateFunc(&testUser) XCTAssertEqual(testUser.name, "默认名称") }
方案3:无需执行即可区分闭包的特殊场景
如果业务场景需要在不执行闭包的前提下区分返回的闭包类型,可以给返回值包裹一层带标识的结构体,不影响原有调用逻辑的同时提升可测试性:
struct Action { enum ActionType { case trueCase, falseCase } let type: ActionType let execute: () -> Void } // 改造原有的返回函数 func someFunction(flag: Bool) -> Action { if flag { return Action(type: .trueCase, execute: { print("True") }) } else { return Action(type: .falseCase, execute: { print("False") }) } }
测试时可以直接校验标识字段,也可以继续执行校验行为:
func testActionType() { let action = someFunction(flag: true) XCTAssertEqual(action.type, .trueCase) // 也可继续执行校验输出 let output = captureStandardOutput { action.execute() } XCTAssertEqual(output, "True") }
内容的提问来源于stack exchange,提问作者gmoraleda
相关产品推荐
相关产品推荐

