- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我有一个要使用 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
}
}
现在进行测试。我想测试 method()
实际上做了它应该做的事情,但我不想调用 callMethod()
中的代码(因为它做了一些可怕的异步/网络/线程的东西不应该在 MyClass 的测试下运行,并且会使测试崩溃)。
所以我基本上想做的是:
SingletonClass = MockSingletonClass: SingletonClass {
override func callMethod() {
// Do nothing
}
let myObject = MyClass()
myObject.method()
// Check if tests passed
这显然不是有效的 Swift,但你明白了。我如何才能仅针对此特定测试覆盖 callMethod()
,使其无害?
编辑: 我尝试使用依赖注入(inject)的形式解决这个问题,但遇到了大问题。我创建了一个自定义初始化方法,仅用于测试,这样我就可以像这样创建我的对象:
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()
}
}
将测试代码与其余代码混合是我觉得有点不愉快的事情,但没关系。最大的问题是我在我的项目中有两个这样构造的单例,它们都相互引用:
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 的 +shared_
风格单例和 Swift 类常量单例通常提供两种行为:
alloc
/init
额外的实例,而应用程序依赖于开发人员遵循通过独占访问共享实例的约定类方法。)行为 #1 偶尔有用,而行为 #2 只是具有设计模式文凭的全局行为。
我会通过完全删除全局变量来解决您的冲突。始终注入(inject)您的依赖项,而不仅仅是为了测试,并考虑当您需要一些东西来协调您正在注入(inject)的任何共享资源集时,您的应用程序中暴露的责任。
在整个应用程序中注入(inject)依赖项的第一步通常很痛苦; “但我到处都需要这个实例!”。使用它作为重新考虑设计的提示,为什么这么多组件访问相同的全局状态以及如何对其建模以提供更好的隔离?
在某些情况下,您需要一些可变共享状态的单个副本,而单例实例可能是最好的实现。但是我发现在大多数例子中仍然不成立。开发人员通常在寻找共享状态,但有一些条件:在连接外部显示器之前只有一个屏幕,只有一个用户在他们注销并进入第二个帐户之前只有一个网络请求队列,在您发现需要进行身份验证之前只有一个网络请求队列vs 匿名请求。同样,在执行下一个测试用例之前,您通常需要一个共享实例。
考虑到使用可失败初始化器(或返回现有共享实例的 obj-c init 方法)的“单例”似乎很少,开发人员似乎很乐意按照惯例共享此状态,所以我认为没有理由不注入(inject)共享对象并编写易于测试的类,而不是使用全局变量。
关于swift - 在 Swift 中模拟单例/sharedInstance,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35094734/
在此代码中,当我键入 GIDSignIn.sharedInstance 时,出于某种原因 sharedInstance 不是函数,这意味着我无法访问 clientID。我在任何地方都找不到解决方案。
在我的代码中,我初始化了两个单例。我有 var session = AVAudioSession.sharedInstance 和 var audioSession = AVAudioSession.
我正在将项目转换为 SDK。我需要将几个实例方法转换为类方法。我收到有关使用“self”的编译器警告。警告是“使用 Class 表达式初始化 Store* 的不兼容指针类型。此 Store 类是单例
假设我有以下单例: @interface ABCSingleton: NSObject @property (nonatomic, copy) NSString *name; @property (n
所有单例函数都是两次调用。“Prova”函数和定时器的选择器是两次调用。 class Timer { static let sharedInstanceTimer = Timer()
我正在创建一个音频应用程序,我希望音频在屏幕关闭时继续播放。(它在屏幕打开时在后台运行良好。) 在 xcode 上启用了后台模式(我在 Info.plist 中设置了“音频”值。)并且我实现了“AVA
我不久前在我的 iOS 项目中集成了 Digits by Fabric 以通过电话号码对用户进行身份验证,直到最近我的应用程序开始崩溃时一切正常,原因是我们在项目中初始化 Digits 的以下代码行:
我有一个小型 iOS 应用程序 (xcode8 ios10),我需要用户使用 facebook 或 google 登录。我已经完成了 facebook 部分,并且效果很好。然后今天我添加了谷歌登录支持
具有以下单例: class Colors { static let sharedInstance = Colors() private init() {} let mainCo
sharedInstance 究竟是什么?我是说有什么用? 目前我在 2 个不同文件之间进行通信时遇到了一些问题。 这是我的问题: 我有 1 个文件调用 A.h/A.m 和另一个文件调用 B.h/B.
我们正在使用键值观察来查看音频输出路由是否通过以下代码进行了更改: AVAudioSession *audioSession = [AVAudioSession sharedInstance]; [a
我正在尝试在我的 iOS 应用程序中使用 TwitterKit 框架(在 Swift 中)。但是,当我使用 Twitter.sharedInstance().APIClient 时,会出现错误,提示“
我已经阅读了谷歌文档来寻求答案,我也查看了一些类似的帖子,但这些都没有帮助我解决我的问题。我正在尝试将 UIButton 设置为 google 登录的触发器。由于某种原因,它压碎了。我当然会添加我编写
我想从一些代码中展示一个 MFMailComposerViewController,这些代码深藏在可通过共享实例访问的实用程序类中。 当我尝试时 [self presentViewController
使用以下代码,我得到了输出音量,但它确实不一致 - 有时它给出相同的值,有时它是一个落后的音量变化,尽管系统音量实际上是正确变化的。 有什么办法让它每次都输出正确的值? func viewDidLoa
我有一个要使用 XCTest 测试的类,这个类看起来像这样: public class MyClass: NSObject { func method() { // Do s
我正在将一些自 iOS 9.0 以来已弃用的 UIAlertViews 更新为 UIAlertViewControllers。 使用 UIAlertView,可以从任何正在执行的代码中抛出警报——即使
我正在创建一个快速框架。其中一个类如下所示。 import Foundation @objc public class classA: NSObject { public overr
我遇到了类似于 this one 的问题。基本上每次我的应用启动时,我都必须使用我的 Google 帐户登录。 然后,我有这个属性: var isGoogleSessionOpen: Bool {
我在 sharedInstance return Static.instance! 行发生以下崩溃: EXC_BREAKPOINT 0x0000000100da42d8 崩溃发生在没有调试断点的 Ad
我是一名优秀的程序员,十分优秀!