'Swift 5.5 test async Task in init

I would like to test if my init function works as expected. There is an async call in the init within a Task {} block. How can I make my test wait for the result of the Task block?

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)   
            }
        }
    }  
}

Test:

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])
}

I tried this, but setting the items, only happens after the XCTWaiter. The compiler warns that XCTWaiter cannot be called with await, because it isn't async.

    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)
    }


Solution 1:[1]

Tnx to matt this is the correct way. No need for async in the test function and just using a predicate did the job.

    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)
    }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Bob Voorneveld