swift - `tearDown` 调用是必要的吗?
问题描述
有事考虑我很久。假设我们已经编写了测试类:
final class BearerTokenManagerTests: XCTestCase {
private var bearerTokenManager: BearerTokenManager!
private var bearerTokenProvider: BearerTokenProvider!
private var stubKeyValueStore: KeyValueStoreDummyStub!
private var scheduler: TestScheduler!
private var disposeBag: DisposeBag!
override func setUp() {
super.setUp()
stubKeyValueStore = KeyValueStoreDummyStub()
bearerTokenProvider = BearerTokenProvider(keyValueStore: stubKeyValueStore)
bearerTokenManager = BearerTokenManager(bearerTokenProvider: bearerTokenProvider)
scheduler = TestScheduler(initialClock: 0)
disposeBag = DisposeBag()
}
override func tearDown() {
stubKeyValueStore = nil
bearerTokenProvider = nil
bearerTokenManager = nil
scheduler = nil
disposeBag = nil
super.tearDown()
}
func test_bearerToken_observeChanges() {
let bearerToken = scheduler.createObserver(BearerTokenManagerType.BearerToken.self)
bearerTokenManager.bearerToken
.bind(to: bearerToken)
.disposed(by: disposeBag)
scheduler.start()
// every update should be saved in key value store
bearerTokenManager.update(bearerToken: "123")
XCTAssertEqual(stubKeyValueStore.string(forKey: "BearerToken"), "123")
bearerTokenManager.update(bearerToken: "456")
XCTAssertEqual(stubKeyValueStore.string(forKey: "BearerToken"), "456")
bearerTokenManager.update(bearerToken: "789")
XCTAssertEqual(stubKeyValueStore.string(forKey: "BearerToken"), "789")
// every udpate should be emited
XCTAssertEqual(bearerToken.events, [
.next(0, nil), // by default (on start) token equal to nil
.next(0, "123"),
.next(0, "456"),
.next(0, "789"),
])
}
}
是否tearDown
有必要要求清洁目的?
为什么我认为没有必要:
- 在每个下一个测试用例
setUp
重置一切之前。 - 当测试
BearerTokenManagerTests
结束时,一切都应该解除分配
为什么我不确定
- 假设“当测试
BearerTokenManagerTests
结束时,一切都应该解除分配”可能是错误的 - 我担心
RxScheduler
副作用 - 我还不知道的东西
有人可以分享他们的经验吗?你清理里面的东西tearDown
吗?重置属性是否setUp
足够?
解决方案
快速回答
根据这篇文章:https ://qualitycoding.org/xctestcase-teardown/
XCTest
XCTestCase
为每个单独的测试调用创建一个新实例,但在完成后不会deinit
任何一个。
演示
我在 Xcode 11.7 中创建了演示应用程序,行为仍然相同。
被测系统
import UIKit
var counter = 0
class ViewController: UIViewController {
init() {
super.init(nibName: nil, bundle: nil)
counter += 1;
print("Created ViewController, currently living: \(counter)")
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
deinit {
counter -= 1;
print("Destroyed ViewController, currently living: \(counter)")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
测试用例tearDown()
class ViewControllerTests: XCTestCase {
var vc: ViewController!
override func setUp() {
super.setUp()
vc = ViewController()
}
override func tearDown() {
vc = nil
super.tearDown()
}
func test_a() {
XCTAssert(true == true)
}
func test_b() {
XCTAssert(true == true)
}
func test_c() {
XCTAssert(true == true)
}
}
输出:
Test Suite 'ViewControllerTests' started at 2020-09-13 14:44:15.889
Test Case '-[DemoTests.ViewControllerTests test_a]' started.
Created ViewController, currently living: 1
Destroyed ViewController, currently living: 0
Test Case '-[DemoTests.ViewControllerTests test_a]' passed (0.001 seconds).
Test Case '-[DemoTests.ViewControllerTests test_b]' started.
Created ViewController, currently living: 1
Destroyed ViewController, currently living: 0
Test Case '-[DemoTests.ViewControllerTests test_b]' passed (0.000 seconds).
Test Case '-[DemoTests.ViewControllerTests test_c]' started.
Created ViewController, currently living: 1
Destroyed ViewController, currently living: 0
Test Case '-[DemoTests.ViewControllerTests test_c]' passed (0.000 seconds).
Test Suite 'ViewControllerTests' passed at 2020-09-13 14:44:15.891.
Executed 3 tests, with 0 failures (0 unexpected) in 0.002 (0.003) seconds
Test Suite 'sdadasTests.xctest' passed at 2020-09-13 14:44:15.892.
Executed 3 tests, with 0 failures (0 unexpected) in 0.002 (0.003) seconds
Test Suite 'Selected tests' passed at 2020-09-13 14:44:15.892.
Executed 3 tests, with 0 failures (0 unexpected) in 0.002 (0.004) seconds
没有测试用例tearDown()
class ViewController2Tests: XCTestCase {
var vc: ViewController!
override func setUp() {
vc = ViewController()
}
func test_a() {
XCTAssert(true == true)
}
func test_b() {
XCTAssert(true == true)
}
func test_c() {
XCTAssert(true == true)
}
}
输出:
Test Suite 'ViewController2Tests' started at 2020-09-13 14:47:43.067
Test Case '-[sdadasTests.ViewController2Tests test_a]' started.
Created ViewController, currently living: 1
Test Case '-[DemoTests.ViewController2Tests test_a]' passed (0.001 seconds).
Test Case '-[DemoTests.ViewController2Tests test_b]' started.
Created ViewController, currently living: 2
Test Case '-[DemoTests.ViewController2Tests test_b]' passed (0.000 seconds).
Test Case '-[DemoTests.ViewController2Tests test_c]' started.
Created ViewController, currently living: 3
Test Case '-[DemoTests.ViewController2Tests test_c]' passed (0.000 seconds).
Test Suite 'ViewController2Tests' passed at 2020-09-13 14:47:43.070.
Executed 3 tests, with 0 failures (0 unexpected) in 0.002 (0.003) seconds
Test Suite 'sdadasTests.xctest' passed at 2020-09-13 14:47:43.070.
Executed 3 tests, with 0 failures (0 unexpected) in 0.002 (0.003) seconds
Test Suite 'Selected tests' passed at 2020-09-13 14:47:43.071.
Executed 3 tests, with 0 failures (0 unexpected) in 0.002 (0.004) seconds
长答案
就像您在示例中看到的那样,没有tearDown()
初始化内部setUp()
不会将新对象分配给相同的属性。deinit
每个测试都会创建单独的实例,并且在完成后不会。只有整个XCTestCase
实例的末尾才会被释放。
在小型项目中,它可能没那么重要。
但是,如果您有很多测试,XCTestCase
并且您正在其中创建大量数据setUp()
(例如,需要大量内存的存根),您应该考虑使用tearDown()
,因为每个测试都会保留它自己的数据副本,setUp()
直到整体XCTestCase
完成并且您最终可能会遇到内存限制问题。
推荐阅读
- c# - MongoDB连接格式和在c#中的正确使用
- pyqt - PyQt5 和 Qt Designer Preview 之间的显着风格差异
- r - > install.packages("ElmStatLearn")
- python - TFF RuntimeError:尝试在不构建函数的情况下捕获 EagerTensor
- swift - 测试运行程序在完成运行测试之前以代码 9 退出
- python - 将单个值附加到存储在 csv 文件中的数据帧
- android - 在 LazyColumns 中滚动 LazyRows 以及粘性行和列?
- python - Asnycpg PostgreSQL 连接问题
- arrays - 如何在 mongoDB 中更新嵌套数组
- r - 将匹配 NA 的 df 的字符行与所有内容进行比较,并根据比较创建新列或 df