I'm writing unit test for my codes which using Combine framework
i have a async operation, so i use expectation(descrption:)
to wait async operation
this is my code example
class MyViewModel {
private var someOtherSubject: CurrentValueSubject<...>(...)
var someOtherStream: ... {
return someOtherSubject.eraseToAnyPublisher()
}
init(...) {
.
.
bind()
}
private func bind() {
.
.
.
someOfMySubject
.flatMap { someAsyncOperation }
.sink { [weak self] ... in
self?.someOtherSubject.send(value2)
}
.store(..)
}
.
.
func handleSomeUserAction() {
self.someOtherSubject.send(value1)
self.someOfMySubject.send(someEvent)
}
}
i'm trying to check value which emitted by someOtherStream
when handleSomeUserAction
is called
so i wrote test code like this
func test_handleSomeUserAction() {
// given
let expectation = expectation(description: "...")
var result: ValueType?
// when
sut.someOtherStream
.dropFirst() // drop CurretValueSubject's default value
.sink {
result = $0
expectation.fulfill()
}
.store(...)
sut.handleSomeUserAction()
// then
wait(for: [expectation], timeout: 1.0)
let unwrapped = try XCTUnwrap(result)
XCTAssertEqual(unwrapped, value1)
}
i know expectedFulfillmentCount
is 1 because it's default value is 1
but test is failed and Xcode show me this message
API violation - multiple calls made to -[XCTestExpectation fulfill]
so i tried to debug, and i figured out expectation.fulfill()
called twice despite of expectedFulfillmentCount
is 1
and result is value2
not value1
that i expected
why this happend? i think async operation(flatMap {...}
) is finished too fast and fulfill
called twice.
i tried delay mock method but i think this is not the right solution