Skip to content

Commit d3eca68

Browse files
committed
Merge branch 'develop'
2 parents 9455570 + 12bdb89 commit d3eca68

File tree

2 files changed

+77
-27
lines changed

2 files changed

+77
-27
lines changed

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ let package = Package(
1717
],
1818
dependencies: [
1919
.package(name: "SnapshotTesting", url: "https://github.com/pointfreeco/swift-snapshot-testing.git", .upToNextMajor(from: "1.8.2")),
20-
.package(name: "SwiftRex", url: "https://github.com/SwiftRex/SwiftRex.git", .upToNextMajor(from: "0.8.1"))
20+
.package(name: "SwiftRex", url: "https://github.com/SwiftRex/SwiftRex.git", .upToNextMajor(from: "0.8.2"))
2121
],
2222
targets: [
2323
.target(

Sources/TestingExtensions/UseCaseTests.swift

Lines changed: 76 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,59 @@ import Foundation
1313
@testable import SwiftRex
1414
import XCTest
1515

16-
public enum Step<ActionType, StateType> {
17-
case send(
18-
ActionType,
19-
file: StaticString = #file,
20-
line: UInt = #line,
21-
stateChange: (inout StateType) -> Void = { _ in }
22-
)
23-
case receive(
24-
ActionType,
16+
public struct SendStep<ActionType, StateType> {
17+
public init(
18+
action: @autoclosure @escaping () -> ActionType,
2519
file: StaticString = #file,
2620
line: UInt = #line,
27-
stateChange: (inout StateType) -> Void = { _ in }
28-
)
21+
stateChange: @escaping (inout StateType) -> Void = { _ in }
22+
) {
23+
self.action = action
24+
self.file = file
25+
self.line = line
26+
self.stateChange = stateChange
27+
}
28+
29+
let action: () -> ActionType
30+
let file: StaticString
31+
let line: UInt
32+
let stateChange: (inout StateType) -> Void
33+
}
34+
35+
public struct ReceiveStep<ActionType, StateType> {
36+
public init(isExpectedAction: @escaping (ActionType) -> Bool,
37+
file: StaticString = #file,
38+
line: UInt = #line,
39+
stateChange: @escaping (inout StateType) -> Void = { _ in }) {
40+
self.isExpectedAction = isExpectedAction
41+
self.file = file
42+
self.line = line
43+
self.stateChange = stateChange
44+
}
45+
46+
public init(action: @autoclosure @escaping () -> ActionType,
47+
file: StaticString = #file,
48+
line: UInt = #line,
49+
stateChange: @escaping (inout StateType) -> Void = { _ in }
50+
) where ActionType: Equatable {
51+
self.init(
52+
isExpectedAction: { $0 == action() },
53+
file: file,
54+
line: line,
55+
stateChange: stateChange
56+
)
57+
}
58+
59+
let file: StaticString
60+
let line: UInt
61+
let stateChange: (inout StateType) -> Void
62+
63+
let isExpectedAction: (ActionType) -> Bool
64+
}
65+
66+
public enum Step<ActionType, StateType> {
67+
case send(SendStep<ActionType, StateType>)
68+
case receive(ReceiveStep<ActionType, StateType>)
2969
case sideEffectResult(
3070
do: () -> Void
3171
)
@@ -40,7 +80,7 @@ extension XCTestCase {
4080
otherSteps: [Step<M.InputActionType, M.StateType>] = [],
4181
file: StaticString = #file,
4282
line: UInt = #line
43-
) where M.InputActionType == M.OutputActionType, M.InputActionType: Equatable, M.StateType: Equatable {
83+
) where M.InputActionType == M.OutputActionType, M.StateType: Equatable {
4484
assert(
4585
initialValue: initialValue,
4686
reducer: reducer,
@@ -61,7 +101,7 @@ extension XCTestCase {
61101
stateEquating: (M.StateType, M.StateType) -> Bool,
62102
file: StaticString = #file,
63103
line: UInt = #line
64-
) where M.InputActionType == M.OutputActionType, M.InputActionType: Equatable {
104+
) where M.InputActionType == M.OutputActionType {
65105
assert(
66106
initialValue: initialValue,
67107
reducer: reducer,
@@ -81,7 +121,7 @@ extension XCTestCase {
81121
stateEquating: (M.StateType, M.StateType) -> Bool,
82122
file: StaticString = #file,
83123
line: UInt = #line
84-
) where M.InputActionType == M.OutputActionType, M.InputActionType: Equatable {
124+
) where M.InputActionType == M.OutputActionType {
85125
var state = initialValue
86126
var middlewareResponses: [M.OutputActionType] = []
87127
let gotAction = XCTestExpectation(description: "got action")
@@ -92,16 +132,21 @@ extension XCTestCase {
92132
}
93133
middleware.receiveContext(getState: { state }, output: anyActionHandler)
94134

95-
steps.forEach { step in
135+
steps.forEach { outerStep in
96136
var expected = state
97137

98-
switch step {
99-
case let .send(action, file, line, stateChange):
138+
switch outerStep {
139+
case let .send(step)://action, file, line, stateChange):
140+
let file = step.file
141+
let line = step.line
142+
let stateChange = step.stateChange
143+
100144
if !middlewareResponses.isEmpty {
101145
XCTFail("Action sent before handling \(middlewareResponses.count) pending effect(s)", file: file, line: line)
102146
}
103147

104148
var afterReducer: AfterReducer = .doNothing()
149+
let action = step.action()
105150
middleware.handle(
106151
action: action,
107152
from: .init(file: "\(file)", function: "", line: line, info: nil),
@@ -111,8 +156,12 @@ extension XCTestCase {
111156
afterReducer.reducerIsDone()
112157

113158
stateChange(&expected)
114-
ensureStateMutation(equating: stateEquating, statusQuo: state, expected: expected)
115-
case let .receive(action, file, line, stateChange):
159+
ensureStateMutation(equating: stateEquating, statusQuo: state, expected: expected, step: outerStep)
160+
case let .receive(step)://action, file, line, stateChange):
161+
let file = step.file
162+
let line = step.line
163+
let stateChange = step.stateChange
164+
116165
if middlewareResponses.isEmpty {
117166
_ = XCTWaiter.wait(for: [gotAction], timeout: 0.2)
118167
}
@@ -121,19 +170,19 @@ extension XCTestCase {
121170
break
122171
}
123172
let first = middlewareResponses.removeFirst()
124-
XCTAssertEqual(first, action, file: file, line: line)
173+
XCTAssertTrue(step.isExpectedAction(first), file: file, line: line)
125174

126175
var afterReducer: AfterReducer = .doNothing()
127176
middleware.handle(
128-
action: action,
177+
action: first,
129178
from: .init(file: "\(file)", function: "", line: line, info: nil),
130179
afterReducer: &afterReducer
131180
)
132-
reducer.reduce(action, &state)
181+
reducer.reduce(first, &state)
133182
afterReducer.reducerIsDone()
134183

135184
stateChange(&expected)
136-
ensureStateMutation(equating: stateEquating, statusQuo: state, expected: expected)
185+
ensureStateMutation(equating: stateEquating, statusQuo: state, expected: expected, step: outerStep)
137186
case let .sideEffectResult(execute):
138187
execute()
139188
}
@@ -145,11 +194,12 @@ extension XCTestCase {
145194
}
146195
}
147196

148-
private func ensureStateMutation<StateType>(
197+
private func ensureStateMutation<ActionType, StateType>(
149198
equating: (StateType, StateType) -> Bool,
150199
statusQuo: StateType,
151200
expected: StateType,
152-
file: StaticString = #file,
201+
step: Step<ActionType, StateType>,
202+
file: StaticString = #filePath,
153203
line: UInt = #line
154204
) {
155205
XCTAssertTrue(
@@ -160,7 +210,7 @@ extension XCTestCase {
160210
dump(expected, to: &expectedString, name: nil, indent: 2)
161211
let difference = diff(old: expectedString, new: stateString) ?? ""
162212

163-
return "Expected state different from current state\n\(difference)"
213+
return "Expected state after step \(step) different from current state\n\(difference)"
164214
}(),
165215
file: file,
166216
line: line

0 commit comments

Comments
 (0)