Skip to content

Commit 46f03a9

Browse files
committed
Improve API
1 parent 3c33649 commit 46f03a9

File tree

1 file changed

+43
-12
lines changed

1 file changed

+43
-12
lines changed

Sources/TestingExtensions/CombineTesting.swift

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,19 @@ import Combine
1010
import XCTest
1111

1212
extension 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, *)
113140
public 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, *)
142172
extension 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

Comments
 (0)