Swift 台风加载情节串连板似乎以编程方式执行异步实例化而不阻塞
我正在开发一个iOS应用程序,并试图将Typhone集成到测试中。我目前正在尝试模拟来自情节提要的视图控制器中的依赖项,因此在我的程序集中:Swift 台风加载情节串连板似乎以编程方式执行异步实例化而不阻塞,swift,unit-testing,storyboard,xcode7,typhoon,Swift,Unit Testing,Storyboard,Xcode7,Typhoon,我正在开发一个iOS应用程序,并试图将Typhone集成到测试中。我目前正在尝试模拟来自情节提要的视图控制器中的依赖项,因此在我的程序集中: public dynamic var systemComponents: SystemComponents! public dynamic func storyboard() -> AnyObject { return TyphoonDefinition.withClass(TyphoonStoryboard.self) {
public dynamic var systemComponents: SystemComponents!
public dynamic func storyboard() -> AnyObject {
return TyphoonDefinition.withClass(TyphoonStoryboard.self) {
(definition) in
definition.useInitializer("storyboardWithName:factory:bundle:") {
(initializer) in
initializer.injectParameterWith("Main")
initializer.injectParameterWith(self)
initializer.injectParameterWith(NSBundle.mainBundle())
}
}
}
我想创建一个CameraModeViewController
(我正在进行单元测试的类),它依赖于提供模拟协议的系统摄像机函数。依赖项是动态变量cameraProvider:camerapiprovider?。我想我正确地创建了一个替换协作程序集来替换systemComponents
MockSystemComponents
是覆盖函数的SystemComponents
的子类。这是我注入模拟的地方:
let assembly = ApplicationAssembly().activateWithCollaboratingAssemblies([
MockSystemComponents(camera: true)
])
let storyboard = assembly.storyboard()
subject = storyboard.instantiateViewControllerWithIdentifier("Camera-Mode") as! CameraModeViewController
测试中的下一行代码是let\uu=subject.view
,我学到了调用viewdiload
并获取所有故事板链接的iboutlet的技巧,本测试需要其中一个
然而,我得到了一个非常神秘的结果:有时,但并非总是,所有的测试都会失败,因为在viewDidLoad
中,我调用了依赖项(cameraProvider
),并收到一个“发送到类的未识别消息”错误。该错误似乎表明,在发送消息时(这是协议CameraAPIProvider
中的正确实例方法),该字段当前是一个类而不是实例:它将消息解释为错误消息中报告的+[mocksystemcamerastreamlayer]
~~~但是~~~
关键是:如果我在对assembly.storyboard()
和subject.view
的调用之间添加一个断点,测试总是通过的。一切都已正确设置,并且消息已正确发送到实例,而无需这种“类方法”虚假解释。因此,我想知道Typhone是否在注入过程中执行了某种我必须等待的异步过程?可能仅在处理故事板交付的视图控制器时?如果是这样的话,有没有办法确保它会阻塞
在Typhone的源代码中挖掘了一段时间后,我得到的印象是,在TyphondDefinition(实例生成器)
initializeInstanceWithArgs:factory:
方法中,有一个\u块id实例
暂时是一个类类型,然后被该类型的实例替换;这可能可以在不阻塞的情况下异步调用,所以注入的成员保留为类类型
更新:为
MockSystemComponents(摄像头:)
添加代码。请注意,SystemComponents继承自TyphonaAssembly
@objc
public class MockSystemComponents: SystemComponents {
var cameraAvailable: NSNumber
init(camera: NSNumber) {
self.cameraAvailable = camera
super.init()
}
public override func systemCameraProvider() -> AnyObject {
return TyphoonDefinition.withClass(MockSystemCamera.self) {
(definition) in
definition.useInitializer("initWithAvailable:") {
(initializer) in
initializer.injectParameterWith(self.cameraAvailable)
}
}
}
}
更新#2:我尝试用属性注入替换
MockSystemComponents.systemCameraProvider()
中的构造函数注入。不同的问题,但我怀疑它的原因是相同的:现在,当我打开它时,被注入的属性(声明为可选的)在某些时候仍然是nil
(但不总是-可能有大约4/5的测试运行失败,与以前大致相同)
更新#3:尝试使用以下代码块,根据使用factory构造(请注意,直接设置factory并不像OP那样有效,但我认为我正确地使用了响应Jasper添加的功能)。结果与使用属性注入(如上面的Update#2)时相同,因此没有骰子。事实上,这个问题甚至在调用实例化之前就出现了。事实上,问题在于程序集通常不是有状态的。有几种方法可以解决这个问题,但不推荐使用我使用的方法——使用成员变量和初始值设定方法。这样做的问题是,在
activateWithCollaborationAssemblies
方法中,会枚举程序集的所有实例方法进行定义,并且会在协作程序集上调用初始化器。因此,即使使用初始值设定项创建程序集,也可能会再次使用伪值调用它
请注意,出现异步行为的原因实际上是定义的组装顺序不确定(存储在NSDictionary中的属性)。这意味着如果
activatewithcollaborationassemblies
碰巧枚举了首先依赖于状态的方法,它们将正常工作;但是,如果先枚举初始值设定项,并且状态被销毁,则之后创建的定义将被删除。事实上,这个问题甚至在调用实例化之前就出现了。事实上,问题在于程序集通常不是有状态的。有几种方法可以解决这个问题,但不推荐使用我使用的方法——使用成员变量和初始值设定方法。这样做的问题是,在activateWithCollaborationAssemblies
方法中,会枚举程序集的所有实例方法进行定义,并且会在协作程序集上调用初始化器。因此,即使使用初始值设定项创建程序集,也可能会再次使用伪值调用它
请注意,出现异步行为的原因实际上是定义的组装顺序不确定(存储在NSDictionary中的属性)。这意味着如果
activatewithcollaborationassemblies
碰巧枚举了首先依赖于状态的方法,它们将正常工作;但是,如果先枚举初始值设定项,并且状态被销毁,则之后创建的定义将被删除。InstanceBuilder使用一些块,但它们不是异步的。什么是MockSystemComponents(摄影机:true)
?你能发布消息来源吗?@jasperbluse-upd