测试模块时重置单例状态

时间:2018-10-23 19:47:36

标签: swift xctest

我在一个迅速的项目中有一个单身人士(是的,我知道人们不喜欢那些人,但是如果您能暂时过去,我将不胜感激)。

我正在编写一些单元测试,这些单元测试不测试该单例,但是它们测试的功能取决于该单例的状态。单例使用静态let声明,并且构造函数无论如何都是私有的,因此像这样重置它不是一个选择。

如果我正在运行一个单元测试,只需在setUp()方法中设置一个单例从中读取的变量,就可以正确设置它,但是当我尝试对整个模块运行测试时,使用第一个调用它的setUp()进行设置,然后在此之后不再进行实例化。因此,基本上整个模块都处于一种状态,这对我来说没有意义-我希望所有功能在测试之间都会重置。

是否有一种方法可以强制XCTest重置测试空间,以确保在每次运行新测试文件时(而不是在其移至新模块时)重置此单例?

4 个答案:

答案 0 :(得分:1)

我不确定这是一种好方法还是正确的方法,但是当我想测试我的单例课程时就使用了它。在单元测试类中,考虑为单例类添加一个扩展,并在此扩展中添加一个名为reset的方法。在此方法中实现单例的重置逻辑,然后在测试类的tearDown函数中调用单例的此重置方法。示例代码:

import XCTest
@testable import MyModule

class MySingletonTests {
    override func setUp() {
        //
    } 

    override func tearDown() {
        MySingleton.shared.reset()
    }

    func testSomething() {
        //
    }
}

extension MySingleton {
    func reset() {
        // reset logic
    }
}

答案 1 :(得分:0)

您得到的信息违反了Dependency Inversion Principle

或者换句话说:

  • 创建单例正在实现的协议(只需声明将用作协议的所有方法和属性,并声明单例类使其符合该协议)

  • 每种需要使用单例的类型都将在其初始参数列表中获得键入为协议的参数,或者具有作为协议类型的属性。在这两种情况下,都将单例保存到该属性。

  • 在创建需要单例的对象时,将其传递。

  • 您可以使用默认参数,一些非常有用的语法糖。这样一来,您就可以通过单身人士,而无需一遍又一遍地指定它。

  • 在单元测试中,您传递了一个实现协议但仅模拟其功能的对象-所谓的mock

答案 2 :(得分:0)

Singletons是依赖项注入的一种形式。虽然不是一个很棒的人,但是仍然是DI。要重新获得控制权而不立即更改您的实现,请添加一种方法来重置您的单身人士。它可以更改其胆量,也可以释放静态实例。

然后从tearDown()调用此方法。

(然后,您可以将单例作为协议进行传递,而不是让叶子模块伸出并抓住它。)

答案 3 :(得分:0)

抛开单例问题,您应该能够通过在nil中将其设置为tearDown()来在每个测试开始时重新实例化Singleton(前提是将其引用计数设为0)。

override func tearDown() {
    singleton = nil
    super.tearDown()
}

运行多个测试时只会看到此问题的原因是,由于每个测试都有自己的测试类实例,因此您无法覆盖上一个测试的剩余状态。

  

对于每个类,测试都是通过运行类设置方法开始的。对于每种测试方法,将分配该类的新实例并执行其实例设置方法。

当您自己运行一个测试时,将创建您的测试类的一个实例,并且单例被初始化的次数与存在测试的次数相同。但是,当您运行整个测试类时,将为该类中的每个测试创建一个测试类实例(5个测试= 5个测试类实例),但是单例仅初始化一次,而不是初始化5次。每个其他测试类实例都是在上一个测试完成运行之后创建的,但是会持续到测试类完成运行为止。对于非单例对象,这不是问题,因为将为每个测试创建一个新的,不相关的实例,但是由于您使用的是Singleton,同一实例在测试中仍然存在,因此您没有机会重新初始化除非它被释放。只有第一个测试才能将单例初始化为所需状态,因此所有测试都将失败。

有关更多信息,请参见here