Swift 台风加载情节串连板似乎以编程方式执行异步实例化而不阻塞

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) {

我正在开发一个iOS应用程序,并试图将Typhone集成到测试中。我目前正在尝试模拟来自情节提要的视图控制器中的依赖项,因此在我的程序集中:

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