我有以下问题。我想在执行所有测试类之前执行一段代码。例如:我不希望我的游戏在执行期间使用SoundEngine单例,而是使用SilentSoundEngine。我想在一次测试中没有激活SilentSoundEngine。我的所有测试都是这样的:
class TestBasketExcercise : XCTestCase {
override func setUp() {
SilentSoundEngine.activate () // SoundEngine is a singleton
}
// The tests
}
CNC中 大多数答案都是针对TestCase提供自定义超类。我正在寻找一种更通用,更清晰的方式来提供所有测试都需要执行的环境。是不是有一个“主要”功能/ Appdelegate喜欢功能的地方进行测试?
答案 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点:
正如@Martin R在对this answer的问题的评论中正确提到的那样,对于Swift 1.2(现在是古代历史:D)和{{},不可能覆盖+load
。 1}}在Swift 3中不再可用。
当你尝试使用dispatch_once()
时,Xcode(&gt; = 8)总是很聪明,并建议你应该使用延迟初始化的全局变量。
当然,术语dispatch_once
往往让每个人都沉迷于恐惧和恐慌,但你当然可以通过将它们设为私有/文件私有来限制它们的范围(对于文件级声明也是如此),所以你不要t污染你的命名空间。
global
打印:
这将完成一次。它不会返回结果。
这将完成一次。它返回一个结果 0
导致
1
导致
2
导致
3
导致
4
导致
5
结果
旁注: 有趣的是,私有let在循环开始之前进行评估,你可以看到,因为如果不是这样的话,那么0就是第一次打印。当您对循环进行注释时,它仍将打印前两行(即评估let) 但是,我猜这是游乐场特定的行为,因为如所述here和here所述,全局变量通常在第一次被引用时被初始化,因此当你注释掉循环时不应该对它们进行求值
(这实际上解决了第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:
注意您必须使用完全限定的类名(即与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