我正在开发一个iOS应用程序,并试图将Typhoon集成到测试中。我目前正在尝试模拟来自情节提要的视图控制器中的依赖项,因此在我的程序集中使用:
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
(我正在进行单元测试的类),它依赖于模拟出的提供系统相机功能的协议。依赖项是dynamic var cameraProvider: CameraAPIProvider?
。我认为我正确地创建了一个替换协作装配来替换systemComponents
;MockSystemComponents
是SystemComponents
重写功能的子类。这是我注入模拟的地方:
let assembly = ApplicationAssembly().activateWithCollaboratingAssemblies([
MockSystemComponents(camera: true)
])
let storyboard = assembly.storyboard()
subject = storyboard.instantiateViewControllerWithIdentifier("Camera-Mode") as! CameraModeViewController
测试中的下一行代码是let _ = subject.view
,据我viewDidLoad
所知,这是调用并获取所有与情节提要链接的IBOutlet的技巧,此测试需要其中之一。
但是,我得到的结果非常神秘:有时但并非总是如此,所有测试都会失败,因为在viewDidLoad
调用依赖项(cameraProvider
)后,我收到了“无法识别的消息发送给类”错误。该错误似乎表明在发送消息时(这是协议中的正确实例方法CameraAPIProvider
),该字段当前是CLASS而不是实例:它+[MockSystemCamera cameraStreamLayer]
按错误消息中的报告解释消息。
~~~但是~~~
这里的踢球者:如果我在两个电话之间添加一个断点assembly.storyboard()
和subject.view
,测试总是通过。一切都已正确设置,并且消息已正确发送到实例,而无需使用这种“类方法”伪造的解释。因此,我想知道Typhoon是否在我必须等待的注入过程中执行某种异步过程?可能仅在处理情节提要提供的视图控制器时?如果是这样,是否有任何方法可以确保其阻止?
深入研究Typhoon的源代码一段时间后,我得到的印象是,该TyphoonDefinition(Instance Builder)
initializeInstanceWithArgs:factory:
方法中有一个__block id instance
临时为Class类型的对象,然后被该类型的实例替换;并且可能可以不阻塞地异步调用它,因此注入的成员保留为Class类型?
更新:添加的代码MockSystemComponents(camera:)
。请注意,SystemComponents继承自TyphoonAssembly
。
@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:尝试使用以下代码块,根据此答案使用工厂构造(请注意,直接设置工厂不能像OP那样起作用,但我认为我正确使用了响应Jasper问题而添加的功能)。结果与使用属性注入(如上面的Update#2)相同,因此没有骰子。
实际上,甚至在调用实例化之前就已经出现了这个问题。事实上,这个问题是组件不通常意欲状态。有几种方法可以解决此问题,但是不建议使用我使用的一种方法-具有成员变量和初始化方法。这样做的问题在于,在activateWithCollaboratingAssemblies
方法中,将枚举程序集的所有实例方法以进行定义,并且实际上将在协作程序集上调用初始化程序。因此,即使您使用初始化程序创建程序集,也可能会使用虚假值再次调用该程序集。
请注意,出现异步行为的原因实际上是定义的汇编存在不确定性的顺序(将其存储在NSDictionary中的属性)。这意味着,如果activateWithCollaboratingAssemblies
碰巧先枚举依赖状态的方法,它们会很好地工作;但是,如果首先枚举了初始化程序,并且销毁了状态,则创建之后的定义将被取消。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句