Ответ
Основной инструмент: XCTest с XCTestExpectation Позволяет дождаться выполнения асинхронной операции.
func testAsyncNetworkCall() {
// 1. Создаем expectation с описанием
let expectation = XCTestExpectation(description: "Fetch user data")
// 2. Запускаем асинхронный код
networkService.fetchUser(id: "123") { result in
// 3. Проверяем результат
switch result {
case .success(let user):
XCTAssertEqual(user.name, "John")
case .failure:
XCTFail("Expected successful response")
}
// 4. Сигнализируем о завершении
expectation.fulfill()
}
// 5. Ждем выполнения expectation с таймаутом
wait(for: [expectation], timeout: 5.0)
}
Другие подходы и инструменты:
-
Тестирование Combine publishers: Использование
XCTestExpectationс подпиской (sink) или специализированных операторов вродеcollect()иfirst(). -
XCTWaiter для сложных сценариев: Позволяет ждать нескольких expectations и проверять их статус.
let waiter = XCTWaiter() let result = waiter.wait(for: [exp1, exp2], timeout: 10) XCTAssertEqual(result, .completed) // Проверяем, что все expectations выполнены -
Сторонние фреймворки:
- Quick/Nimble: Предоставляют более выразительный синтаксис (
expect(...).toEventually(equal(...))). - Mocking frameworks (Mockingbird, Cuckoo): Упрощают создание моков для асинхронных зависимостей.
- Quick/Nimble: Предоставляют более выразительный синтаксис (
Best Practices:
- Всегда устанавливайте разумный timeout.
- Не забывайте вызывать
fulfill()во всех путях выполнения (успех и ошибка). - Для тестов, зависящих от времени (таймеры, debounce), используйте моки часов (
TestSchedulerв Combine).