Как сказал @Asperi: причина этой ошибки в том, что вы получаете значения асинхронно. Я немного искал и нашел учебник Apple о XCTestExpectation
использовании. Поэтому я попытался использовать его с вашим кодом, и тесты прошли успешно. Другой способ - использовать Объединить ожидания .
class StackoverFlowQuestionTests: XCTestCase {
var model = UserViewModel()
override func setUp() {
model = UserViewModel()
}
func testIsValid() throws {
let expectation = self.expectation(description: "waiting validation")
let subscriber = model.$isValid.sink { _ in
guard self.model.username != "" else { return }
expectation.fulfill()
}
model.username = "1234"
wait(for: [expectation], timeout: 1)
XCTAssertTrue(model.isValid)
}
func testIsNotValid() {
let expectation = self.expectation(description: "waiting validation")
let subscriber = model.$isValid.sink { _ in
guard self.model.username != "" else { return }
expectation.fulfill()
}
model.username = "1"
wait(for: [expectation], timeout: 1)
XCTAssertFalse(model.isValid)
}
}
ОБНОВЛЕНИЕ Я добавляю весь код и вывод для ясности. Я изменил тестирование, как в вашем примере (где вы тестируете опции «1» и «1234»). И вы увидите, что я просто скопировал и вставил вашу модель (кроме имени и public
для переменных и init()
). Но, тем не менее, у меня нет этой ошибки:
Асинхронное ожидание не выполнено: превышено время ожидания в 1 секунду с невыполненными ожиданиями: "ожидание проверки".
// MARK: TestableCombineModel.swift file
import Foundation
import Combine
public class TestableModel: ObservableObject {
@Published public var username = ""
@Published public var isValid = false
private var disposables = Set<AnyCancellable>()
public init() {
$username
.receive(on: RunLoop.main) // as you see, I didn't delete it
.removeDuplicates()
.map { input in
print("~~~> \(input.count >= 3)")
return input.count >= 3
}
.assign(to: \.isValid, on: self)
.store(in: &disposables)
}
}
// MARK: stackoverflowanswerTests.swift file:
import XCTest
import stackoverflowanswer
import Combine
class stackoverflowanswerTests: XCTestCase {
var model: TestableModel!
override func setUp() {
model = TestableModel()
}
func testValidation() throws {
let expectationSuccessfulValidation = self.expectation(description: "waiting successful validation")
let expectationFailedValidation = self.expectation(description: "waiting failed validation")
let subscriber = model.$isValid.sink { _ in
// look at the output. at the first time there will be "nothing"
print(self.model.username == "" ? "nothing" : self.model.username)
if self.model.username == "1234" {
expectationSuccessfulValidation.fulfill()
} else if self.model.username == "1" {
expectationFailedValidation.fulfill()
}
}
model.username = "1234"
wait(for: [expectationSuccessfulValidation], timeout: 1)
XCTAssertTrue(model.isValid)
model.username = "1"
wait(for: [expectationFailedValidation], timeout: 1)
XCTAssertFalse(model.isValid)
}
}
и вот вывод
2020-01-14 09:16:41.207649+0600 stackoverflowanswer[1266:46298] Launching with XCTest injected. Preparing to run tests.
2020-01-14 09:16:41.389610+0600 stackoverflowanswer[1266:46298] Waiting to run tests until the app finishes launching.
Test Suite 'All tests' started at 2020-01-14 09:16:41.711
Test Suite 'stackoverflowanswerTests.xctest' started at 2020-01-14 09:16:41.712
Test Suite 'stackoverflowanswerTests' started at 2020-01-14 09:16:41.712
Test Case '-[stackoverflowanswerTests.stackoverflowanswerTests testValidation]' started.
nothing
~~~> true
1234
~~~> false
1
Test Case '-[stackoverflowanswerTests.stackoverflowanswerTests testValidation]' passed (0.004 seconds).
Test Suite 'stackoverflowanswerTests' passed at 2020-01-14 09:16:41.717.
Executed 1 test, with 0 failures (0 unexpected) in 0.004 (0.005) seconds
Test Suite 'stackoverflowanswerTests.xctest' passed at 2020-01-14 09:16:41.717.
Executed 1 test, with 0 failures (0 unexpected) in 0.004 (0.005) seconds
Test Suite 'All tests' passed at 2020-01-14 09:16:41.718.
Executed 1 test, with 0 failures (0 unexpected) in 0.004 (0.006) seconds
ОБНОВЛЕНИЕ 2 На самом деле я ловлю ошибки " Асинхронное ожидание не удалось: ...", если я изменил эту строку кода:
let subscriber = model.$isValid.sink { _ in
на это, как предлагает Xcode:
model.$isValid.sink { _ in // remove "let subscriber ="