首页 > 解决方案 > `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有必要要求清洁目的?

为什么我认为没有必要:

为什么我不确定

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

标签: swiftxcodexctestcaserxtest

解决方案


快速回答

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

XCTestXCTestCase为每个单独的测试调用创建一个新实例,但在完成后不会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完成并且您最终可能会遇到内存限制问题。


推荐阅读