跨包闭包抛出的Error未被Catch捕获,仅测试环境复现求助
问题:跨包定义的Error在测试环境中无法被正确捕获?
我正在测试函数中抛出的错误是否能被正确捕获,核心代码如下:
Package A 中的错误定义
public enum SomeError: Int, Error { case errorOne = 1 case errorTwo = 2 }
Package B 中的业务代码
struct SomeStruct { let service: SomeService func functionThatShouldCatch() throws -> String { do { let result = try service.getSomething() } catch SomeError.errorOne { // 预期在此处捕获错误 } return "All ok" } }
Mock 服务类
class MockService: Service { var getSomethingHandler: (() throws -> String)? = nil }
测试代码
@Test func testingAllOk() throws { mockService.getSomethingHandler = { throw SomeError.errorOne } let result = try sut.functionThatShouldCatch() // <- 测试失败,此处捕获到了SomeError.errorOne #expect(result == "All ok") }
我省略了无关代码,只保留核心内容。想请教是配置有误,还是这种在闭包中抛出外部包Error的方式本身就无法正常工作?
另外,当我修改SomeStruct的代码,添加一个通用的catch分支后:
func functionThatShouldCatch() throws -> String { do { let result = try service.getSomething() } catch SomeError.errorOne { // 预期在此处捕获错误 } catch { print(error) // <- 输出内容为SomeError.errorOne } return "All ok" }
通用分支能打印出SomeError.errorOne,但专门针对errorOne的分支还是没触发。
更新
我发现问题关键在于SomeError的定义位置:当SomeError和错误捕获代码在同一个包中时,测试能正常捕获错误;但放在单独的外部包时,测试就失败,重命名也没用。奇怪的是,应用运行时这个逻辑是正常的,只有测试环境会出问题。
解答
这种情况大概率是测试目标的依赖配置问题,而非语言本身的限制,可从以下几个方向排查:
- 检查测试Target的依赖:确保测试Target正确引入Package A,且没有重复导入(比如同时引入源码和预编译框架),避免运行时错误实例的类型与捕获分支中的类型属于不同模块。
- 给
SomeError添加Equatable实现:Swift的Error协议默认不要求实现Equatable,跨包时可能因类型匹配逻辑缺失导致分支捕获失败,添加后可明确匹配规则:
public enum SomeError: Int, Error, Equatable { case errorOne = 1 case errorTwo = 2 }
- 显式指定模块名:在捕获分支中用模块名限定错误类型,比如
catch PackageA.SomeError.errorOne,规避模块解析或命名冲突问题。 - 对比类型一致性:在测试的通用catch分支中打印
type(of: error)和SomeError.errorOne的类型,确认二者是否完全一致。
总结来说,应用运行正常但测试失败,通常是测试环境的依赖或模块解析逻辑与主应用不一致导致的,而非跨包抛出错误本身的问题。
内容的提问来源于stack exchange,提问作者Bob Voorneveld
相关产品推荐
相关产品推荐

