【问题标题】:Trouble mocking singleton for unit testing in Swift在 Swift 中模拟单例以进行单元测试时遇到问题
【发布时间】:2016-03-21 12:01:26
【问题描述】:

您好,我正在尝试模拟我用来测试各种视图控制器是否正确调用它的方法的单例。

我有这样声明的单身人士

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

    [...]
}

在使用单例的视图控制器中,它被设置为一个惰性计算属性:

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() 断言中我得到Value of type 'ModelsManager' has no member 'flag'

我在这里缺少什么?

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

【问题讨论】:

  • 你是在继承单例吗?如果您尝试将其设置在同一类中而不是子类中,我认为您会有更好的运气。然后,如果您在视图控制器中将其设置为惰性,则不需要在 testSomething() 方法中再次设置它。
  • 如果我不设置,那么ViewController将如何使用ModelsManagerMock呢?
  • 我不会那样子类化它。您的单例方法返回的是 ModelsManager 的实例,而不是 ModelsManagerMock。如果您需要将其编写为单例,则需要重写单例方法以返回正确的类。在 Obj-C 中,使用实例类型很容易。我会看看是否有办法在 Swift 中做到这一点。
  • 如果名为 TEST 的预处理器标志为真,则不将 ModelsManagerinit 方法设为私有,从而部分解决了此问题。然后我不做vc.Models = mockModelsManager,而是做let mock = ModelsManagerMock(); vc.Models = mock。如果可能的话,我仍在寻找一个编译时解决方案......

标签: ios swift swift2 xctest


【解决方案1】:

正确的解决方案必须涉及不继承您的单例。使用私有 init 方法创建单例会禁止您继承此方法。

如果目标是测试单例的当前功能,为什么要为其添加额外的功能?单例的关键点是应该只有一个。如果你想支持多个,你不应该让它成为一个单例,即使它只是为了测试。

【讨论】:

  • 我想测试我的控制器是否调用了我的单例的特定方法。除了以子类的形式创建一个模拟单例之外,我想不出任何其他方法来做到这一点。在原始 Singleton 中添加公共方法和标志会使它无缘无故地“变脏”。你对如何测试这个有什么其他建议吗?
  • 您可以将它们标记为内部并使用@testable 属性developer.apple.com/library/ios/documentation/DeveloperTools/…
  • 基本上这个答案说单例与单元测试不兼容。我不同意。我看到的问题是惰性实例化。您可以改用依赖注入。 ...或者最坏的情况,在你的 viewDidLoad 中设置单例,然后给它你的模拟对象。有关依赖注入的更多信息,请参阅sharpfivesoftware.com/2015/02/03/testing-singletons-in-swift
  • 单例与单元测试完全兼容。它正在创建单例的自定义版本,这就是这种情况下的问题。我不认为应该为任何单元测试创​​建新对象(除了测试对象本身),而不仅仅是单例。
猜你喜欢
  • 1970-01-01
  • 2010-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-25
  • 1970-01-01
  • 2015-07-25
相关资源
最近更新 更多