如何在执行任何XCTest之前运行一次性设置代码

时间:2015-04-23 11:44:20

标签: ios swift xctest xctestcase

我有以下问题。我想在执行所有测试类之前执行一段代码。例如:我不希望我的游戏在执行期间使用SoundEngine单例,而是使用SilentSoundEngine。我想在一次测试中没有激活SilentSoundEngine。我的所有测试都是这样的:

class TestBasketExcercise : XCTestCase {        
    override func setUp() {
        SilentSoundEngine.activate () // SoundEngine is a singleton
    }
    // The tests 
}

CNC中 大多数答案都是针对TestCase提供自定义超类。我正在寻找一种更通用,更清晰的方式来提供所有测试都需要执行的环境。是不是有一个“主要”功能/ Appdelegate喜欢功能的地方进行测试?

4 个答案:

答案 0 :(得分:54)

来自Writing Test Classes and Methods

  

您可以选择为类设置添加自定义方法    之前运行的(+ (void)setUp)和拆解(+ (void)tearDown)   并且在课堂上的所有测试方法之后。

在Swift中,class方法:

override class func setUp() {
    super.setUp()
    // Called once before all tests are run
}

override class func tearDown() {
    // Called once after all tests are run
    super.tearDown()
}

答案 1 :(得分:44)

<强> TL; DR:
如上所述here,您应该在测试目标Info.plist中声明一个NSPrincipalClass。执行此类的init中的所有一次性设置代码,因为“XCTest在加载测试包时自动创建该类的单个实例”,因此所有一次性设置代码将在加载时执行一次测试包。

更详细一点:

首先在编辑中回答这个想法:

Afaik,测试包没有main(),因为测试被注入到正在运行的主目标中,因此您必须将一次性设置代码添加到main()使用编译时(或至少是运行时)检查目标是否用于运行测试的主目标。如果没有这个检查,你可能会在正常运行目标时激活SilentSoundEngine,我认为这是不可取的,因为类名意味着这个声音引擎不会产生任何声音,老实说,谁想要这个? :)

然而,有一个类似AppDelegate的功能,我会在答案结束时(如果你不耐烦,它在标题“另一个(更多XCTest特定的)方法”下)。

现在,让我们将这个问题分成两个核心问题:

  1. 运行测试时,如何确保您想要执行一次的代码实际上正在执行一次
  2. 哪里你应该执行该代码,所以感觉不像是ugly hack所以它只是工作而你不必考虑它并记住每次写的必要步骤一个新的测试套件
  3. 关于第1点:

    正如@Martin R在对this answer的问题的评论中正确提到的那样,对于Swift 1.2(现在是古代历史:D)和{{},不可能覆盖+load。 1}}在Swift 3中不再可用。

    一种方法

    当你尝试使用dispatch_once()时,Xcode(&gt; = 8)总是很聪明,并建议你应该使用延迟初始化的全局变量。 当然,术语dispatch_once往往让每个人都沉迷于恐惧和恐慌,但你当然可以通过将它们设为私有/文件私有来限制它们的范围(对于文件级声明也是如此),所以你不要t污染你的命名空间。

    Imho,他们实际上是一个非常漂亮的模式(仍然,剂量使毒药...),看起来像这样,例如:

    global

    打印:

      

    这将完成一次。它不会返回结果。
      这将完成一次。它返回一个结果   0
      导致
      1
      导致
      2
      导致
      3
      导致
      4
      导致
      5
      结果

    旁注: 有趣的是,私有let在循环开始之前进行评估,你可以看到,因为如果不是这样的话,那么0就是第一次打印。当您对循环进行注释时,它仍将打印前两行(即评估let) 但是,我猜这是游乐场特定的行为,因为如所述herehere所述,全局变量通常在第一次被引用时被初始化,因此当你注释掉循环时不应该对它们进行求值

    另一种(更具XCTest特定的)方法

    (这实际上解决了第1点和第2点......)

    正如Cupertino的公司所说here,有一种方法可以运行一次性预测试设置代码。

    要实现这一点,您需要创建一个虚拟安装类(可能称之为TestSetup?)并将所有一次性安装代码放入其init:

    private let _doSomethingOneTimeThatDoesNotReturnAResult: Void = {
        print("This will be done one time. It doesn't return a result.")
    }()
    
    private let _doSomethingOneTimeThatDoesReturnAResult: String = {
        print("This will be done one time. It returns a result.")
        return "result"
    }()
    
    for i in 0...5 {
        print(i)
        _doSomethingOneTimeThatDoesNotReturnAResult
        print(_doSomethingOneTimeThatDoesReturnAResult)
    }
    

    注意该类必须继承自NSObject,因为Xcode尝试使用class TestSetup: NSObject { override init() { SilentSoundEngine.activate() } } 实例化“该类的单个实例”,所以如果该类是纯Swift类,这将发生:

    +new

    然后,将此类声明为test-bundles Info.plist文件中的PrincipalClass: Declaring the PrincipalClass in Info.plist

    注意您必须使用完全限定的类名(即与TestSetup相比的YourTestTargetsName.TestSetup),因此Xcode(感谢,zneak找到该类。 ..)。

    如XCTestObservationCenter的文档中所述,“XCTest在加载测试包时会自动创建该类的单个实例”,因此在加载测试时,所有一次性安装代码都将在TestSetup的init中执行-bundle。

答案 2 :(得分:3)

如果为您的测试用例构建一个超类,那么您可以在超类中运行通用设置,并在子类中执行您可能需要的任何特定设置。我对Obj-C比Swift更熟悉,并且还没有机会对此进行测试,但这应该很接近。

// superclass
class SuperClass : XCTestCase {        
    override func setUp() {
        SilentSoundEngine.activate () // SoundEngine is a singleton
    }
}

// subclass
class Subclass : Superclass {
    override func setUp() {
        super.setup()
    }
}

答案 3 :(得分:2)

如果您只想为该类中的所有UI测试调用一次setUp方法,则可以在XCode 11中调用override class func setUp()

此方法是Apple文档中用于UI测试的一部分: https://developer.apple.com/documentation/xctest/xctestcase/understanding_setup_and_teardown_for_test_methods