Introduce timeout and after to support delayed verification.
I introduced these two global functions to support delayed verification discussed in this issue(#277).
// timeout waits until CallMatcher::matches returns true and exits on success.
public func timeout(_ timeoutDuration: TimeInterval, waitingDuration: TimeInterval = 0.01) -> ContinuationWithTimeout
// after waits until delayDuration elapsed and exits when CallMatcher::matches never return true.
public func after(_ delayDuration: TimeInterval, waitingDuration: TimeInterval = 0.01) -> ContinueationAfterDelay
ContinuationWithTimeout and ContinueationAfterDelay conform to Continuation protocol.
Continuation determines whether CallMatcher::matches should be called again, waits, etc.
like this:
// MockManager
public func verify<IN, OUT>(_ method: String, callMatcher: CallMatcher, parameterMatchers: [ParameterMatcher<IN>], continuation: Continuation, sourceLocation: SourceLocation) -> __DoNotUse<IN, OUT> {
...
while continuation.check() {
calls = []
indexesToRemove = []
for (i, stubCall) in stubCalls.enumerated() {
if let stubCall = stubCall as? ConcreteStubCall<IN> , (parameterMatchers.reduce(stubCall.method == method) { $0 && $1.matches(stubCall.parameters) }) {
calls.append(stubCall)
indexesToRemove.append(i)
}
}
matches = callMatcher.matches(calls)
if !matches && !callMatcher.canRecoverFromFailure(calls) {
break
}
if matches && continuation.exitOnSuccess {
break
}
continuation.wait()
}
...
return __DoNotUse()
}
Users can simply write test of fire-and-forget asynchronous function and asynchronous function with completion closure that does not require argument verification.
stub(mockTestClass) { stub in
when(stub.f()).thenDoNothing()
}
// someMethod is expected to call f three times within three seconds.
mockTestClass.someMethod()
verify(mockTestClass, timeout(3).times(3)).f()
But I think that if users want to verify completion closure argument, They should use expectation.
stub(mockTestClass) { stub in
when(stub.f()).thenDoNothing()
}
let expectation = XCTestExpectation(description: #function)
mockTestClass.someMethod() { i in
XCTAssertEqual(i, 1)
expectation.fulfill()
}
wait(for: [expectation], timeout: 3)
verify(mockTestClass, times(3)).f()
If users write below code, XCTAssertEqual may no be called.
stub(mockTestClass) { stub in
when(stub.f()).thenDoNothing()
}
mockTestClass.someMethod() { i in
XCTAssertEqual(i, 1)
}
verify(mockTestClass, timeout(3).times(3)).f()
This PR is a prototype. If this PR is acceptable, I will write comments, tests and explanation to README.md.
If there are any improvements, please point them out.
I like the API this provides, but I need some more context on the implementation before we go ahead with it.