如何在执行任何 XCTest 之前 运行 一次性设置代码
How to run one-time setup code before executing any XCTest
我有以下问题。我想在执行所有测试 类 之前执行一段代码。例如:我不希望我的游戏在执行期间使用 SoundEngine 单例,而是 SilentSoundEngine。我想激活 SilentSoundEngine 一次,而不是在所有测试中。我所有的测试都是这样的:
class TestBasketExcercise : XCTestCase {
override func setUp() {
SilentSoundEngine.activate () // SoundEngine is a singleton
}
// The tests
}
-编辑-
大多数答案都针对为 TestCase 提供自定义超类。我正在寻找一种更通用、更简洁的方法来提供所有测试都需要执行的环境。某处是否有类似 "main" 函数/Appdelegate 的功能用于测试?
来自Writing Test Classes and Methods:
You can optionally add customized methods for class setup
(+ (void)setUp)
and teardown (+ (void)tearDown)
as well, which run before
and after all of the test methods in the class.
在 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()
}
如果您为测试用例构建一个超类,那么您可以 运行 在超类中进行通用设置,然后在子类中执行您可能需要的任何特定设置。我对 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()
}
}
TL;DR:
正如 here 所述,您应该在 test-targets Info.plist 中声明一个 NSPrincipalClass。执行此 class 的 init 中的所有 one-time-setup 代码,因为 "XCTest automatically creates a single instance of that class when the test bundle is loaded",因此您所有的 one-time-setup 代码将在加载 test-bundle 时执行一次。 =30=]
有点冗长:
首先在您的编辑中回答这个想法:
Afaik,测试包没有 main()
,因为测试被注入到您的 运行ning 主目标中,因此您必须将 one-time-setup 代码添加到使用 compile-time(或至少 运行 时间)的主要目标的 main()
检查目标是否用于 运行 测试。如果没有这个检查,当 运行 正常连接目标时,你将有激活 SilentSoundEngine
的风险,我认为这是不可取的,因为 class 名称暗示这个 sound-engine 将产生没有声音,老实说,谁想要那个? :)
但是有一个类似AppDelegate
的功能,我会在我的回答结束时谈到(如果你不耐烦,它在header "Another (more XCTest-specific) approach"下面) .
现在,让我们把这个问题分成两个核心问题:
- 当 运行 测试实际执行 一次时,如何确保您要执行的代码 正好执行一次 当 运行 进行测试时
- 你应该在哪里执行那个代码,所以它感觉不像一个ugly hack,所以它不需要你去想它就可以工作并记住必要的每次编写新测试套件时的步骤
关于第 1 点:
正如@Martin R 在他对 this answer 对你的问题的评论中正确提到的那样,从 Swift 1.2 开始,不再可能覆盖 +load
(这是现在的古老历史: D),并且 dispatch_once()
在 Swift 中不再可用 3.
一种方法
无论如何,当您尝试使用 dispatch_once
时,Xcode (>=8) 总是非常聪明,并建议您应该使用延迟初始化的全局变量。
当然,术语 global
往往会让每个人都陷入恐惧和恐慌,但你当然可以通过使它们成为 private/fileprivate 来限制它们的范围(对 file-level 声明也是如此),这样你就不会污染你的命名空间。
恕我直言,它们实际上是一个非常漂亮的图案(不过,剂量是毒药......)可以看起来像这样,例如:
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)
}
这会打印:
This will be done one time. It doesn't return a result.
This will be done one time. It returns a result.
0
result
1
result
2
result
3
result
4
result
5
result
旁注:
有趣的是,私有 let 甚至在循环开始之前就已被评估,您可以看到这一点,因为如果不是这样,0 将是第一个打印。当您将循环注释掉时,它仍会打印前两行(即评估 lets)。
但是,我猜这是游乐场特定的行为,因为正如 here and here 所述,全局变量通常在第一次在某处被引用时初始化,因此当您注释掉循环时不应评估它们。
另一种(更多XCTest-specific)方法
(这实际上解决了第 1 点和第 2 点...)
正如来自 Cupertino 的公司所说 here,有一种方法可以 运行 one-time-pre-testing 设置代码。
为了实现这一点,您创建了一个虚拟 setup-class(也许称它为 TestSetup?)并将所有一次性设置代码放入其 init:
class TestSetup: NSObject {
override init() {
SilentSoundEngine.activate()
}
}
注意 class 必须从 NSObject 继承,因为 Xcode 尝试使用 [=21= 实例化 "single instance of that class" ], 所以如果 class 是纯 Swift class, 就会发生:
*** NSForwarding: warning: object 0x11c2d01e0 of class 'YourTestTargetsName.TestSetup' does not implement methodSignatureForSelector: -- trouble ahead
Unrecognized selector +[YourTestTargetsName.TestSetup new]
然后,您在 test-bundle 的 Info.plist 文件中将此 class 声明为 PrincipalClass:
注意 您必须使用完全限定的 class-name(即 YourTestTargetsName.TestSetup 与 TestSetup 相比),因此 class由 Xcode 找到(谢谢,zneak...)。
如 XCTestObservationCenter 的文档所述,"XCTest automatically creates a single instance of that class when the test bundle is loaded",因此当加载 test-bundle.
时,您的所有 one-time-setup 代码将在 TestSetup 的初始化中执行
如果您只想对 class 中的所有 UI 测试调用一次 setUp
方法,在 Xcode 11 中您可以调用 override class func setUp()
此方法是用于 UI 测试的 Apple 文档的一部分:
https://developer.apple.com/documentation/xctest/xctestcase/understanding_setup_and_teardown_for_test_methods
我有以下问题。我想在执行所有测试 类 之前执行一段代码。例如:我不希望我的游戏在执行期间使用 SoundEngine 单例,而是 SilentSoundEngine。我想激活 SilentSoundEngine 一次,而不是在所有测试中。我所有的测试都是这样的:
class TestBasketExcercise : XCTestCase {
override func setUp() {
SilentSoundEngine.activate () // SoundEngine is a singleton
}
// The tests
}
-编辑- 大多数答案都针对为 TestCase 提供自定义超类。我正在寻找一种更通用、更简洁的方法来提供所有测试都需要执行的环境。某处是否有类似 "main" 函数/Appdelegate 的功能用于测试?
来自Writing Test Classes and Methods:
You can optionally add customized methods for class setup
(+ (void)setUp)
and teardown(+ (void)tearDown)
as well, which run before and after all of the test methods in the class.
在 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()
}
如果您为测试用例构建一个超类,那么您可以 运行 在超类中进行通用设置,然后在子类中执行您可能需要的任何特定设置。我对 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()
}
}
TL;DR:
正如 here 所述,您应该在 test-targets Info.plist 中声明一个 NSPrincipalClass。执行此 class 的 init 中的所有 one-time-setup 代码,因为 "XCTest automatically creates a single instance of that class when the test bundle is loaded",因此您所有的 one-time-setup 代码将在加载 test-bundle 时执行一次。 =30=]
有点冗长:
首先在您的编辑中回答这个想法:
Afaik,测试包没有 main()
,因为测试被注入到您的 运行ning 主目标中,因此您必须将 one-time-setup 代码添加到使用 compile-time(或至少 运行 时间)的主要目标的 main()
检查目标是否用于 运行 测试。如果没有这个检查,当 运行 正常连接目标时,你将有激活 SilentSoundEngine
的风险,我认为这是不可取的,因为 class 名称暗示这个 sound-engine 将产生没有声音,老实说,谁想要那个? :)
但是有一个类似AppDelegate
的功能,我会在我的回答结束时谈到(如果你不耐烦,它在header "Another (more XCTest-specific) approach"下面) .
现在,让我们把这个问题分成两个核心问题:
- 当 运行 测试实际执行 一次时,如何确保您要执行的代码 正好执行一次 当 运行 进行测试时
- 你应该在哪里执行那个代码,所以它感觉不像一个ugly hack,所以它不需要你去想它就可以工作并记住必要的每次编写新测试套件时的步骤
关于第 1 点:
正如@Martin R 在他对 this answer 对你的问题的评论中正确提到的那样,从 Swift 1.2 开始,不再可能覆盖 +load
(这是现在的古老历史: D),并且 dispatch_once()
在 Swift 中不再可用 3.
一种方法
无论如何,当您尝试使用 dispatch_once
时,Xcode (>=8) 总是非常聪明,并建议您应该使用延迟初始化的全局变量。
当然,术语 global
往往会让每个人都陷入恐惧和恐慌,但你当然可以通过使它们成为 private/fileprivate 来限制它们的范围(对 file-level 声明也是如此),这样你就不会污染你的命名空间。
恕我直言,它们实际上是一个非常漂亮的图案(不过,剂量是毒药......)可以看起来像这样,例如:
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)
}
这会打印:
This will be done one time. It doesn't return a result.
This will be done one time. It returns a result.
0
result
1
result
2
result
3
result
4
result
5
result
旁注:
有趣的是,私有 let 甚至在循环开始之前就已被评估,您可以看到这一点,因为如果不是这样,0 将是第一个打印。当您将循环注释掉时,它仍会打印前两行(即评估 lets)。
但是,我猜这是游乐场特定的行为,因为正如 here and here 所述,全局变量通常在第一次在某处被引用时初始化,因此当您注释掉循环时不应评估它们。
另一种(更多XCTest-specific)方法
(这实际上解决了第 1 点和第 2 点...)
正如来自 Cupertino 的公司所说 here,有一种方法可以 运行 one-time-pre-testing 设置代码。
为了实现这一点,您创建了一个虚拟 setup-class(也许称它为 TestSetup?)并将所有一次性设置代码放入其 init:
class TestSetup: NSObject {
override init() {
SilentSoundEngine.activate()
}
}
注意 class 必须从 NSObject 继承,因为 Xcode 尝试使用 [=21= 实例化 "single instance of that class" ], 所以如果 class 是纯 Swift class, 就会发生:
*** NSForwarding: warning: object 0x11c2d01e0 of class 'YourTestTargetsName.TestSetup' does not implement methodSignatureForSelector: -- trouble ahead
Unrecognized selector +[YourTestTargetsName.TestSetup new]
然后,您在 test-bundle 的 Info.plist 文件中将此 class 声明为 PrincipalClass:
注意 您必须使用完全限定的 class-name(即 YourTestTargetsName.TestSetup 与 TestSetup 相比),因此 class由 Xcode 找到(谢谢,zneak...)。
如 XCTestObservationCenter 的文档所述,"XCTest automatically creates a single instance of that class when the test bundle is loaded",因此当加载 test-bundle.
时,您的所有 one-time-setup 代码将在 TestSetup 的初始化中执行如果您只想对 class 中的所有 UI 测试调用一次 setUp
方法,在 Xcode 11 中您可以调用 override class func setUp()
此方法是用于 UI 测试的 Apple 文档的一部分: https://developer.apple.com/documentation/xctest/xctestcase/understanding_setup_and_teardown_for_test_methods