Ios Swift中单元测试的单例模拟问题

Ios Swift中单元测试的单例模拟问题,ios,swift,swift2,xctest,Ios,Swift,Swift2,Xctest,您好,我试图模拟一个我用来测试各种视图控制器是否正确调用其方法的单例 我已宣布单身人士为单身人士 public class ModelsManager { static let sharedInstance = ModelsManager() private init() {} [...] } 在使用singleton的视图控制器中,它被设置为惰性计算属性,如下所示: class MyViewController: UIViewController { laz

您好,我试图模拟一个我用来测试各种视图控制器是否正确调用其方法的单例

我已宣布单身人士为单身人士

public class ModelsManager {
    static let sharedInstance = ModelsManager()
    private init() {}

    [...]
}
在使用singleton的视图控制器中,它被设置为惰性计算属性,如下所示:

class MyViewController: UIViewController {
    lazy var Models = {
        return ModelsManager.sharedInstance
    }()

    [...]
}
我试图在XCTestCase中模拟ModelsManager单例:

[...]

func testSomething() {
   let vc = MyViewController(nibName: "MyView", bundle: nil)
   var mockModelsManager = ModelsManagerMock.sharedInstance
   vc.Models = mockModelsManager

   [... do something that calls a function in ModelsManager...]

   expect(mockModelsManager.flag) == true // Using Nimble here
}

class ModelsManagerMock: ModelsManager {
    var flag = false

    override func test() {
        flag = true
    }
}
expect()
断言中,我得到的
类型为'ModelsManager'的值没有成员'flag'

我错过了什么

编辑
似乎我缺少的是ModelsManager,sharedInstance仍然从超类返回
IRModelsManager()
。由于
static
不能被子类覆盖,我该如何解决这个问题呢?

正确的解决方案必须是不要将单例子类化。使用私有init方法创建singleton会禁止您对该方法进行子类化


如果目标是测试singleton的当前功能,为什么要向其添加其他功能?独生子女的关键是,应该只有一个。如果您想支持多个,就不应该将其设置为单例,即使它只是用于测试。

那么您是否要对单例进行子类化?如果您尝试将它设置在同一个类中而不是子类中,我认为您会有更好的运气。然后,如果在视图控制器中将其设置为惰性,则不需要在testSomething()方法中再次设置它。如果我不设置它,那么ViewController将如何使用
ModelsManagerMock
?我不会这样子类化它。您的singleton方法返回的是ModelsManager实例,而不是ModelsManager实例。如果需要将其作为单例编写,则需要重写单例方法以返回正确的类。在Obj-C中,使用实例类型很容易做到这一点。我将看看是否有办法在Swift中实现这一点。如果名为TEST的预处理器标志为true,则不将
ModelsManager
init
方法设为私有,从而部分解决了这一问题。然后,我不做
vc.Models=mockModelsManager
I做
let mock=ModelsManagerMock();vc.Models=mock
。如果可能的话,我仍然在寻找这个问题的编译时解决方案……我想测试我的控制器是否调用我的单例的特定方法。除了以子类的形式创建一个模拟单例,我想不出任何其他方法来实现这一点。在原始单例中添加公共方法和标志将使其毫无理由地“脏”。关于如何测试这个问题,你还有什么其他建议吗?你可以将它们标记为内部的,并使用@testable属性。基本上,这个答案说单例与单元测试不兼容。我不同意。我看到的问题是延迟实例化。您可以使用依赖项注入来代替。。。或者在最坏的情况下,在viewDidLoad中设置singleton,然后将其作为模拟对象。有关依赖注入的更多信息,请参阅。Singleton与单元测试完全兼容。在本例中,问题在于创建一个自定义版本的单例。我不认为应该为任何单元测试创建新对象(测试对象本身除外),而不仅仅是单例测试。