【问题标题】:Is there a way to a @Published property that it's actually getting updated and publishing?有没有办法让 @Published 属性实际上得到更新和发布?
【发布时间】:2020-03-30 08:40:32
【问题描述】:

我有以下类,我想测试如果一个对象订阅了“elapsedTime”发布的属性,它会在属性更改时接收更新。

class SampleSegmentTimer {
    @Published var elapsedTime: DateComponents?
    private var subs = Set<AnyCancellable>()

    func start(tickingEvery interval: TimeInterval = 1) {
        Timer.publish(every: interval, on: .main, in: .common)
            .autoconnect()
            .map(transform)
            .assign(to: \.elapsedTime, on: self)
            .store(in: &subs)
    }

    private func transform(_ date: Date) -> DateComponents {
        // do some calculation
        DateComponents()
    }
}

我想编写如下测试:

func testStart() {
        // Given
        let segmentTimer = SampleSegmentTimer()
        var elapsedTimes = [DateComponents?]()
        let elapsedTimeSub = segmentTimer.$elapsedTime.sink { elapsedTimes.append($0) }

        // When
        segmentTimer.start(tickEvery: 1) // I want to wait 3 seconds to let it tick 3 times

        // Then
        XCTAssertEqual(elapsedTimes.count, 3)
}

【问题讨论】:

  • 首先,测试Combine 框架的功能是没有意义的。其次,制作计时器发布者并将其发布到您自己的已发布发布者是没有意义的。两个出版商? Timer 管道已经是一个发布者。把它放在其他人可以直接订阅的地方。第三,你有一个保留周期和内存泄漏,但现在不要介意。
  • 另外,您不需要映射来测量经过的时间。有一个测量间隔的运算符,或者只是使用scan 来维护间隔或运行总计。最好放弃所有代码并向我们解释您真正想要做什么。
  • 显然我没有说清楚。而不是回应你提到的每一点,我会试着解释我实际在做什么,或者至少是一个简化的版本。我想构建一个计时器应用程序,允许用户启动计时器,继续在后台运行,暂停它,取消它,当然还有结束计时器。当计时器结束时,该段将存储在核心数据中以及其他属性中。时间段(持续时间)应包括后台时间,如果用户多次暂停和恢复,则不包括暂停的持续时间。
  • 好的。可暂停可取消定时器是一个很好解决的问题;你可以在这里找到很多例子。我不知道“继续在后台运行”是什么意思,因为计时器不会在后台运行。 — 如果您想为此使用组合,请取出autoconnect,这样您就可以在计时器开始时调用connect。我没有在您的代码中看到您的暂停计划。
  • 我不希望视图直接连接到计时器。我想使用视图模型。计时器必须跟踪段的开始/结束日期,并且每次触发时,它都必须发布自开始以来的持续时间,不包括任何用户暂停并包括我之前提到的背景时间。应用程序的其他一些部分有时也可以观察计时器,而不仅仅是实际的计时器视图和视图模型。

标签: swift unit-testing combine


【解决方案1】:
func testStart() {
        // Add expectation
        let expectation = XCTestExpectation(description: "tick")
        // Given
        let segmentTimer = SampleSegmentTimer()
        var elapsedTimes = [DateComponents?]()
        let elapsedTimeSub = segmentTimer.$elapsedTime.sink { elapsedTimes.append($0) }

        // When
        segmentTimer.start(tickEvery: 1) // I want to wait 3 seconds to let it tick 3 times

        // Add this line
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) { expectation.fulfill() }

        // Then
        wait(for: [expectation], timeout: 5) // wait for the fulfillment signal
        XCTAssertEqual(elapsedTimes.count, 3)
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-10
    • 2011-04-15
    • 2010-11-15
    • 2021-05-21
    • 1970-01-01
    相关资源
    最近更新 更多