【问题标题】:Swift 5.5 test async Task in initSwift 5.5 在 init 中测试异步任务
【发布时间】:2022-10-18 08:40:27
【问题描述】:

我想测试我的 init 函数是否按预期工作。 Task {} 块内的 init 中有一个异步调用。如何让我的测试等待任务块的结果?

class ViewModel: ObservableObject {
    @Published private(set) var result: [Item]
        
    init(fetching: RemoteFetching) {
        self.result = []
        Task {
            do {
                let result = try await fetching.fetch()
                
                self.result = result // <- need to do something with @MainActor?
            } catch {
                print(error)   
            }
        }
    }  
}

测试:

func testFetching() async {
    let items = [Item(), Item()]
    let fakeFetching = FakeFetching(returnValue: items)

    let vm = ViewModel(fetching: FakeFetching())
        
    XCTAssertEqual(vm.result, [])
        
    // wait for fetching, but how?
        
    XCTAssertEqual(vm.result, items])
}

我试过这个,但设置项目,只发生在 XCTWaiter 之后。编译器警告不能用 await 调用 XCTWaiter,因为它不是异步的。

    func testFetching() async {
        let items = [Item(), Item()]
        let fakeFetching = FakeFetching(returnValue: items)

        let expectation = XCTestExpectation()

        let vm = ViewModel(fetching: FakeFetching())
        
        XCTAssertEqual(vm.result, [])
        
        vm.$items
            .dropFirst()
            .sink { value in
                XCTAssertEqual(value, items)
                expectation.fulfill()
            }
            .store(in: &cancellables)
        
        let result = await XCTWaiter.wait(for: [expectation], timeout: 1)
        
        XCTAssertEqual(result, .completed)
    }

【问题讨论】:

  • 等待和期望是正确的。你只是用错了。基本上你是在想太多了。您不需要async 测试方法。你不需要自己调用实现。您不需要组合链。只需使用谓词期望等待 vm.result 设置。

标签: swift concurrency


【解决方案1】:

期待和等待是正确的。你只是用错了。

你想太多了。您不需要async 测试方法。你不需要自己打电话给fulfill。您不需要组合链。只需使用谓词期望等到设置vm.result

基本上规则是这样的:测试async 方法需要async 测试方法。但是测试碰巧进行异步调用的方法的异步“结果”,例如您的 init 方法,只需要良好的老式期望和等待测试。

我举个例子。这是您的代码的简化版本;该结构与您正在做的基本相同:

protocol Fetching {
    func fetch() async -> String
}
class MyClass {
    var result = ""
    init(fetcher: Fetching) {
        Task {
            self.result = await fetcher.fetch()
        }
    }
}

好的,下面是如何测试它:

final class MockFetcher: Fetching {
    func fetch() async -> String { "howdy" }
}

final class MyLibraryTests: XCTestCase {
    let fetcher = MockFetcher()
    func testMyClassInit() {
        let subject = MyClass(fetcher: fetcher)
        let expectation = XCTNSPredicateExpectation(
            predicate: NSPredicate(block: { _, _ in
                subject.result == "howdy"
            }), object: nil
        )
        wait(for: [expectation], timeout: 2)
    }
}

专家额外:Bool 谓词期望使用起来很常见,因此手头有一个将期望、谓词和等待组合到一个包中的便捷方法会很有用:

extension XCTestCase {
    func wait(
        _ condition: @escaping @autoclosure () -> (Bool),
        timeout: TimeInterval = 10)
    {
        wait(for: [XCTNSPredicateExpectation(
            predicate: NSPredicate(block: { _, _ in condition() }), object: nil
        )], timeout: timeout)
    }
}

结果是,例如,上面的测试代码可以简化为:

    func testMyClassInit() {
        let subject = MyClass(fetcher: fetcher)
        wait(subject.result == "howdy")
    }

确实方便。在我自己的代码中,我经常添加一个显式断言,即使它完全是多余的,只是为了完全清楚我声称我的代码做了什么:

    func testMyClassInit() {
        let subject = MyClass(fetcher: fetcher)
        wait(subject.result == "howdy")
        XCTAssertEqual(subject.result, "howdy") // redundant but nice
    }

【讨论】:

  • 您不能简单地将扩展方法命名为AssertWaitAssertWaitFor 或类似的名称吗?没有冗余也一样清楚。 (如果您可以违反前缀所有权规则,您甚至可以将其命名为 XTCAssertWait。是的,您不应该“抄袭”别人的前缀,但对于消费者而言,在输入 XTC 前缀时,您的前缀会出现作为自动完成建议之一,它对发现非常有帮助,并且可以证明违规行为是正当的,但这是一个偏好/纯粹的事情。)
【解决方案2】:

Tnx 到matt 这是正确的方法。测试函数中不需要异步,只需使用谓词就可以了。

func testFetching() {
    let items = [Item(), Item()]
    let fakeFetching = FakeFetching(returnValue: items)

    let expectation = XCTestExpectation()

    let vm = ViewModel(fetching: FakeFetching())
    
    let pred = NSPredicate { _, _ in
        vm.items == items
    }
    let expectation = XCTNSPredicateExpectation(predicate: pred, object: vm)
    
    wait(for: [expectation], timeout: 1)
}

【讨论】:

  • 您已经继承了expectation 的原始声明。
猜你喜欢
  • 1970-01-01
  • 2012-11-21
  • 1970-01-01
  • 2011-05-21
  • 2019-08-02
  • 1970-01-01
  • 1970-01-01
  • 2017-05-05
  • 2017-07-22
相关资源
最近更新 更多