【问题标题】:Is `tearDown` calling necessary?`tearDown` 调用是必要的吗?
【发布时间】:2020-04-07 01:47:55
【问题描述】:

有些事想我很久了。假设我们已经编写了测试类:

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 中的属性是否足够?

【问题讨论】:

  • 基于主要意见,但在这种情况下,您可能根本不需要tearDown();因为您没有为下一次运行做任何特殊的“重置”。

标签: swift xcode xctestcase rxtest


【解决方案1】:

快速解答

根据这篇文章: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()的TestCase

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

长答案

就像您在示例中看到的那样,在 setUp() 内部没有 tearDown() 的初始化不会将新对象分配给相同的属性。每个测试都会创建单独的实例,并且在完成后不会 deinit 它。只会释放整个 XCTestCase 实例的末尾。

在小型项目中,这可能无关紧要。

但是,如果您在一个 XCTestCase 中有很多测试,并且您在 setUp() 中创建了大量数据(例如占用大量内存的存根),您应该考虑使用 tearDown(),因为每个测试都会保留它拥有从setUp() 到整个XCTestCase 的数据副本,您最终可能会遇到内存限制问题。

【讨论】:

  • 非常有趣,但是您的测试实际上并没有使用setUp 来做任何事情,而且很难看出这与您在此处所说的内容有什么关系。我们实际上并没有从中了解关于setUptearDown任何东西。还要记住,这些方法有一个实例版本和一个类版本。
  • 我会提供更好的例子
  • 好吧,我不知道你是否真的需要。您所展示的是setUptearDown 用于外部性。它们不受这一切的影响,显然你确实希望在每次测试后重置你的外部性,这样事情就不会受到之前测试的影响。我们在这里谈论苹果和橘子。你说的很有意思,但与正确使用setUptearDown毫无关系。
  • 你的结论是错误的。您说“您在 setUp 中创建的每个对象都应在 tearDown 中销毁”。但是,如果您的意思是,就像在您最初的问题中那样,您在 setUp 中配置的每个实例属性,您确实不需要需要在 tearDown 中撤消它,因为您将获得一个新实例和一个新实例下一次测试运行时的实例属性。因此,毕竟,您对原始问题的回答完全错误。
  • @matt 我编辑了我的答案。我提供了更好的例子,我改变了我的结论。
猜你喜欢
  • 2011-01-21
  • 2019-10-19
  • 2010-10-18
  • 2014-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多