在Swift中模拟singleton / sharedInstance
我有一个我想用XCTest测试的类,这个类看起来像这样:
public class MyClass: NSObject { func method() { // Do something... // ... SingletonClass.sharedInstance.callMethod() } } 该类使用以下形式实现的单例: public class SingletonClass: NSObject { // Only accessible using singleton static let sharedInstance = SingletonClass() private override init() { super.init() } func callMethod() { // Do something crazy that shouldn't run under tests } } 现在进行测试.我想测试该方法()实际上做了它应该做的事情,但我不想调用callMethod()中的代码(因为它做了一些不应该在测试中运行的可怕的异步/网络/线程的东西MyClass,并将使测试崩溃). 所以我基本上想做的是: SingletonClass = MockSingletonClass: SingletonClass { override func callMethod() { // Do nothing } let myObject = MyClass() myObject.method() // Check if tests passed 这显然是无效的Swift,但你明白了.我怎样才能为这个特定的测试覆盖callMethod(),使其无害? 编辑:我尝试使用依赖注入的形式解决这个问题,但遇到了大问题.我创建了一个自定义初始化方法,仅用于测试,以便我可以像这样创建我的对象: let myObject = MyClass(singleton: MockSingletonClass) 让MyClass看起来像这样 public class MyClass: NSObject { let singleton: SingletonClass init(mockSingleton: SingletonClass){ self.singleton = mockSingleton } init() { singleton = SingletonClass.sharedInstance } func method() { // Do something... // ... singleton.callMethod() } } 将测试代码与其余代码混合是我觉得有点不愉快,但没关系. BIG的问题是我在我的项目中有两个像这样构建的单身人士,他们互相引用: public class FirstSingletonClass: NSObject { // Only accessible using singleton static let sharedInstance = FirstSingletonClass() let secondSingleton: SecondSingletonClass init(mockSingleton: SecondSingletonClass){ self.secondSingleton = mockSingleton } private override init() { secondSingleton = SecondSingletonClass.sharedInstance super.init() } func someMethod(){ // Use secondSingleton } } public class SecondSingletonClass: NSObject { // Only accessible using singleton static let sharedInstance = SecondSingletonClass() let firstSingleton: FirstSingletonClass init(mockSingleton: FirstSingletonClass){ self.firstSingleton = mockSingleton } private override init() { firstSingleton = FirstSingletonClass.sharedInstance super.init() } func someOtherMethod(){ // Use firstSingleton } } 当第一个使用的单例之一,init方法等待另一个的init方法时,这会造成死锁,依此类推…… 解决方法
你的单身人士遵循Swift / Objective-C代码库中非常常见的模式.正如您所见,它也非常难以测试,也是编写不可测试代码的简便方法.有时单身人士是一个有用的模式,但我的经验是,模式的大多数用途实际上都不适合应用程序的需求.
Objective-C和Swift类常量单例的shared_ style单例通常提供两种行为: >它可能会强制只能实例化一个类的单个实例. (实际上,这通常不会强制执行,您可以继续分配/初始化其他实例,而应用程序依赖于开发人员遵循通过类方法专门访问共享实例的约定.) 行为#1偶尔会有用,而行为#2只是具有设计模式文凭的全球性行为. 我会通过完全删除全局变量来解决你的冲突.一直注入您的依赖项,而不仅仅是用于测试,并考虑当您需要某些东西来协调您注入的任何共享资源集时,在您的应用程序中暴露的责任. 在整个应用程序中注入依赖关系的第一步通常是痛苦的; “但我到处都需要这个实例!??”使用它作为重新考虑设计的提示,为什么这么多组件访问相同的全局状态以及如何建模以提供更好的隔离? 在某些情况下,您需要一些可变共享状态的单个副本,而单例实例可能是最佳实现.但是我发现在大多数例子中仍然不成立.开发人员通常会寻找共享状态,但有一些条件:在连接外部显示器之前只有一个屏幕,只有一个用户在他们注销并进入第二个帐户之前,只有一个网络请求队列,直到您发现需要进行身份验证vs匿名请求.类似地,在执行下一个测试用例之前,您经常需要共享实例. 鉴于“单身”似乎很少使用可用的初始化器(或返回现有共享实例的obj-c初始化方法),似乎开发人员很乐意按惯例共享此状态,因此我认为没有理由不注入共享对象和写容易测试的类而不是使用全局变量. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |