Skip to content

Commit d47149c

Browse files
authored
Merge pull request #33 from Ryu0118/feature/teststore-waitforall
Add `TestStore.waitForAll`
2 parents 921d78d + e761248 commit d47149c

File tree

5 files changed

+109
-3
lines changed

5 files changed

+109
-3
lines changed

Sources/SimplexArchitecture/TestStore.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ public final class TestStore<Reducer: ReducerProtocol> where Reducer.Action: Equ
1111
/// The running state container.
1212
var runningContainer: StateContainer<Reducer.Target>?
1313

14+
/// The running tasks
15+
var runningTasks: [SendTask] = []
16+
1417
/// An array of tested actions.
1518
var testedActions: [ActionTransition<Reducer>] = []
1619

@@ -62,6 +65,18 @@ public final class TestStore<Reducer: ReducerProtocol> where Reducer.Action: Equ
6265
}
6366
}
6467

68+
/// Wait for all of the TestStore's remaining SendTasks to complete.
69+
public func waitForAll() async {
70+
await withTaskGroup(of: Void.self) { group in
71+
for task in runningTasks {
72+
group.addTask {
73+
await task.wait()
74+
}
75+
}
76+
await group.waitForAll()
77+
}
78+
}
79+
6580
/// Asserts an action was received from an effect and asserts how the state changes.
6681
///
6782
/// - Parameters:
@@ -210,6 +225,7 @@ public final class TestStore<Reducer: ReducerProtocol> where Reducer.Action: Equ
210225
let actualContainer = expectedContainer.copy()
211226

212227
let sendTask = target.store.sendIfNeeded(action)
228+
runningTasks.append(sendTask)
213229

214230
expected?(actualContainer)
215231

@@ -241,6 +257,7 @@ public final class TestStore<Reducer: ReducerProtocol> where Reducer.Action: Equ
241257
let actualContainer = expectedContainer.copy()
242258

243259
let sendTask = target.store.sendIfNeeded(action)
260+
runningTasks.append(sendTask)
244261

245262
expected?(actualContainer)
246263

@@ -273,6 +290,7 @@ public final class TestStore<Reducer: ReducerProtocol> where Reducer.Action: Equ
273290
let actualContainer = expectedContainer.copy()
274291

275292
let sendTask = target.store.sendIfNeeded(action)
293+
runningTasks.append(sendTask)
276294

277295
expected?(actualContainer)
278296

@@ -304,6 +322,7 @@ public final class TestStore<Reducer: ReducerProtocol> where Reducer.Action: Equ
304322
let actualContainer = expectedContainer.copy()
305323

306324
let sendTask = target.store.sendIfNeeded(action)
325+
runningTasks.append(sendTask)
307326

308327
expected?(actualContainer)
309328

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@testable import SimplexArchitecture
2+
import XCTest
3+
4+
final class CollectionTests: XCTestCase {
5+
func testSafe() throws {
6+
let array = ["a", "b"]
7+
XCTAssertNotNil(array[safe: 1])
8+
XCTAssertNil(array[safe: 2])
9+
}
10+
}

Tests/SimplexArchitectureTests/ReducerTests.swift

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@testable import SimplexArchitecture
22
import SwiftUI
33
import XCTest
4+
import Dependencies
45

56
@MainActor
67
final class ReducerTests: XCTestCase {
@@ -94,6 +95,43 @@ final class ReducerTests: XCTestCase {
9495
$0.count = 0
9596
}
9697
}
98+
99+
func testDependencies() async {
100+
let testStore = TestView(
101+
store: Store(
102+
reducer: withDependencies {
103+
$0.continuousClock = ImmediateClock()
104+
} operation: {
105+
TestReducer()
106+
},
107+
initialReducerState: .init()
108+
)
109+
).testStore(states: .init())
110+
111+
await testStore.send(.runEffectWithDependencies)
112+
await testStore.receive(.increment) {
113+
$0.count = 1
114+
}
115+
}
116+
117+
func testWaitForAll() async {
118+
let testStore = TestView(
119+
store: Store(
120+
reducer: withDependencies {
121+
$0.continuousClock = ImmediateClock()
122+
} operation: {
123+
TestReducer()
124+
},
125+
initialReducerState: .init()
126+
)
127+
).testStore(states: .init())
128+
129+
await testStore.send(.runEffectWithDependencies)
130+
await testStore.waitForAll()
131+
await testStore.receive(.increment) {
132+
$0.count = 1
133+
}
134+
}
97135
}
98136

99137
private struct TestReducer: ReducerProtocol {
@@ -118,8 +156,11 @@ private struct TestReducer: ReducerProtocol {
118156
case invokeIncrement
119157
case invokeDecrement
120158
case send
159+
case runEffectWithDependencies
121160
}
122161

162+
@Dependency(\.continuousClock) private var clock
163+
123164
func reduce(into state: StateContainer<TestView>, action: ReducerAction) -> SideEffect<Self> {
124165
switch action {
125166
case .incrementFromReducerAction:
@@ -171,6 +212,12 @@ private struct TestReducer: ReducerProtocol {
171212

172213
case .send:
173214
return .send(.increment)
215+
216+
case .runEffectWithDependencies:
217+
return .run { send in
218+
try await clock.sleep(for: .seconds(1))
219+
await send(.increment)
220+
}
174221
}
175222
}
176223
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
@testable import SimplexArchitecture
2+
import XCTest
3+
4+
final class TaskResultTests: XCTestCase {
5+
func testCatching() {
6+
let result1 = TaskResult {
7+
throw CancellationError()
8+
}
9+
switch result1 {
10+
case .success:
11+
XCTFail()
12+
case .failure(let error):
13+
XCTAssertTrue(error is CancellationError)
14+
}
15+
16+
let result2 = TaskResult {}
17+
switch result2 {
18+
case .success:
19+
break
20+
case .failure:
21+
XCTFail()
22+
}
23+
}
24+
}

Tests/SimplexArchitectureTests/TestStoreTests.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,27 @@ final class TestStoreTests: XCTestCase {
66
func testSend() async throws {
77
let store = TestView().testStore(states: .init())
88
XCTAssertNil(store.runningContainer)
9-
await store.send(.increment) {
9+
XCTAssertTrue(store.runningTasks.isEmpty)
10+
let sendTask = await store.send(.increment) {
1011
$0.count = 1
1112
}
13+
XCTAssertEqual(store.runningTasks, [sendTask])
1214
XCTAssertNotNil(store.runningContainer)
1315
}
1416

1517
@MainActor
1618
func testReceive() async throws {
1719
let store = TestView().testStore(states: .init())
18-
await store.send(.receiveTest)
20+
XCTAssertTrue(store.runningTasks.isEmpty)
21+
22+
let sendTask = await store.send(.receiveTest)
1923
XCTAssertTrue(store.testedActions.isEmpty)
24+
XCTAssertEqual(store.runningTasks, [sendTask])
25+
2026
await store.receive(.increment) {
2127
$0.count = 1
2228
}
23-
XCTAssertTrue(store.testedActions.count == 1)
29+
XCTAssertEqual(store.testedActions.count, 1)
2430
}
2531
}
2632

0 commit comments

Comments
 (0)