@@ -10,13 +10,19 @@ import Combine
1010import XCTest
1111
1212extension XCTestCase {
13+
14+ /// Assert for Combine Publishers
15+ /// - Parameters:
16+ /// - publisher: subject-under-test
17+ /// - values: list of values expected to be delivered
18+ /// - andCompletes: whether we expect completion or not
19+ /// - Returns: The operation with expectations. Hold this and by the end of your test case, call .wait giving the timeout
1320 @available ( macOS 10 . 15 , iOS 13 . 0 , tvOS 13 . 0 , watchOS 6 . 0 , * )
1421 public func assert< P: Publisher > (
1522 publisher: P ,
1623 eventuallyReceives values: P . Output ... ,
17- andCompletes: Bool = false ,
18- timeout: TimeInterval
19- ) -> ( ) -> Void where P. Output: Equatable {
24+ andCompletes: Bool = false
25+ ) -> Waiter where P. Output: Equatable {
2026 var collectedValues : [ P . Output ] = [ ]
2127 let valuesExpectation = expectation ( description: " Expected values " )
2228 let completionExpectation = expectation ( description: " Expected completion " )
@@ -37,7 +43,7 @@ extension XCTestCase {
3743 }
3844 )
3945
40- return { [ weak self] in
46+ return Waiter { [ weak self] timeout in
4147 guard let self = self else {
4248 XCTFail ( " Test ended before waiting for expectations " )
4349 return
@@ -48,13 +54,18 @@ extension XCTestCase {
4854 }
4955 }
5056
57+ /// Assert for Combine Publishers
58+ /// - Parameters:
59+ /// - publisher: subject-under-test
60+ /// - values: list of values expected to be delivered
61+ /// - andCompletesWith: type of completion expected to happen (success, failure, an specific type of failure)
62+ /// - Returns: The operation with expectations. Hold this and by the end of your test case, call .wait giving the timeout
5163 @available ( macOS 10 . 15 , iOS 13 . 0 , tvOS 13 . 0 , watchOS 6 . 0 , * )
5264 public func assert< P: Publisher > (
5365 publisher: P ,
5466 eventuallyReceives values: P . Output ... ,
55- andCompletesWith: ValidateCompletion < P . Failure > ,
56- timeout: TimeInterval
57- ) -> ( ) -> Void where P. Output: Equatable {
67+ andCompletesWith: ValidateCompletion < P . Failure >
68+ ) -> Waiter where P. Output: Equatable {
5869 var collectedValues : [ P . Output ] = [ ]
5970 let valuesExpectation = expectation ( description: " Expected values " )
6071 let completionExpectation = expectation ( description: " Expected completion " )
@@ -70,7 +81,7 @@ extension XCTestCase {
7081 }
7182 )
7283
73- return { [ weak self] in
84+ return Waiter { [ weak self] timeout in
7485 guard let self = self else {
7586 XCTFail ( " Test ended before waiting for expectations " )
7687 return
@@ -81,12 +92,16 @@ extension XCTestCase {
8192 }
8293 }
8394
95+ /// Assert for Combine Publishers
96+ /// - Parameters:
97+ /// - publisher: subject-under-test
98+ /// - completesWithoutValues: type of completion expected to happen (success, failure, an specific type of failure)
99+ /// - Returns: The operation with expectations. Hold this and by the end of your test case, call .wait giving the timeout
84100 @available ( macOS 10 . 15 , iOS 13 . 0 , tvOS 13 . 0 , watchOS 6 . 0 , * )
85101 public func assert< P: Publisher > (
86102 publisher: P ,
87- completesWithoutValues: ValidateCompletion < P . Failure > ,
88- timeout: TimeInterval
89- ) -> ( ) -> Void {
103+ completesWithoutValues: ValidateCompletion < P . Failure >
104+ ) -> Waiter {
90105 let completionExpectation = expectation ( description: " Expected completion " )
91106 let cancellable = publisher. sink (
92107 receiveCompletion: { result in
@@ -98,7 +113,7 @@ extension XCTestCase {
98113 }
99114 )
100115
101- return { [ weak self] in
116+ return Waiter { [ weak self] timeout in
102117 guard let self = self else {
103118 XCTFail ( " Test ended before waiting for expectations " )
104119 return
@@ -109,27 +124,42 @@ extension XCTestCase {
109124 }
110125}
111126
127+ public struct Waiter {
128+ let wait : ( TimeInterval ) -> Void
129+
130+ init ( wait: @escaping ( TimeInterval ) -> Void ) {
131+ self . wait = wait
132+ }
133+
134+ public func wait( timeout: TimeInterval ) {
135+ wait ( timeout)
136+ }
137+ }
138+
112139@available ( macOS 10 . 15 , iOS 13 . 0 , tvOS 13 . 0 , watchOS 6 . 0 , * )
113140public struct ValidateCompletion < Failure: Error > {
114141 let isExpected : ( Subscribers . Completion < Failure > ) -> Bool
115142 public init ( validate: @escaping ( Subscribers . Completion < Failure > ) -> Bool ) {
116143 self . isExpected = validate
117144 }
118145
146+ /// Publisher should complete without errors
119147 public static var isSuccess : ValidateCompletion {
120148 ValidateCompletion { result in
121149 guard case . finished = result else { return false }
122150 return true
123151 }
124152 }
125153
154+ /// Publisher should complete with error
126155 public static var isFailure : ValidateCompletion {
127156 ValidateCompletion { result in
128157 guard case . failure = result else { return false }
129158 return true
130159 }
131160 }
132161
162+ /// Publisher should complete with an specific error, validate in the predicate (true means this is what you expect)
133163 public static func failedWithError( _ errorPredicate: @escaping ( Failure ) -> Bool ) -> ValidateCompletion {
134164 ValidateCompletion { result in
135165 guard case let . failure( error) = result else { return false }
@@ -140,6 +170,7 @@ public struct ValidateCompletion<Failure: Error> {
140170
141171@available ( macOS 10 . 15 , iOS 13 . 0 , tvOS 13 . 0 , watchOS 6 . 0 , * )
142172extension ValidateCompletion where Failure: Equatable {
173+ /// Publisher should complete with an specific error, give the Equatable Failure type to compare against
143174 public static func failedWithError( _ expectedError: Failure ) -> ValidateCompletion {
144175 ValidateCompletion { result in
145176 guard case let . failure( error) = result else { return false }
0 commit comments