`tearDown` 调用是必要的吗?

Is `tearDown` calling necessary?

有件事考虑我很久了。假设我们已经编写了测试 class:

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 打扫卫生有必要吗?

为什么我认为没有必要:

为什么我不确定

有人可以分享他们的经验吗?你清理 tearDown 中的东西吗?重置 setUp 中的属性是否足够?

快速回答

根据这篇文章:https://qualitycoding.org/xctestcase-teardown/

XCTest creates a new XCTestCase instance for every individual test invocation but doesn't deinit any of them after completion.

演示

我在 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 完成,您最终可能会遇到内存限制问题。