Skip to content

Commit df307eb

Browse files
Make the new XCTModify play nicely with non-exhaustive testing. (#1939)
* Make the new XCTModify play nicely with non-exhaustive testing. * wip * Update Tests/ComposableArchitectureTests/TestStoreNonExhaustiveTests.swift Co-authored-by: Stephen Celis <[email protected]> --------- Co-authored-by: Stephen Celis <[email protected]>
1 parent 3258ec3 commit df307eb

File tree

4 files changed

+84
-3
lines changed

4 files changed

+84
-3
lines changed

Package.resolved

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ let package = Package(
2020
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
2121
.package(url: "https://github.com/google/swift-benchmark", from: "0.1.0"),
2222
.package(url: "https://github.com/pointfreeco/combine-schedulers", from: "0.8.0"),
23-
.package(url: "https://github.com/pointfreeco/swift-case-paths", from: "0.10.0"),
23+
.package(url: "https://github.com/pointfreeco/swift-case-paths", from: "0.13.0"),
2424
.package(url: "https://github.com/apple/swift-collections", from: "1.0.2"),
2525
.package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "0.7.0"),
2626
.package(url: "https://github.com/pointfreeco/swift-dependencies", from: "0.1.2"),

Sources/ComposableArchitecture/TestStore.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
@_spi(Internals) import CasePaths
12
import Combine
23
import CustomDump
34
import Foundation
@@ -1168,6 +1169,13 @@ extension TestStore where ScopedState: Equatable {
11681169
) throws {
11691170
let current = expected
11701171
var expected = expected
1172+
let updateStateToExpectedResult = updateStateToExpectedResult.map { original in
1173+
{ (state: inout ScopedState) in
1174+
try XCTModifyLocals.$isExhaustive.withValue(self.exhaustivity == .on) {
1175+
try original(&state)
1176+
}
1177+
}
1178+
}
11711179

11721180
switch self.exhaustivity {
11731181
case .on:
@@ -1745,6 +1753,14 @@ extension TestStore where ScopedState: Equatable {
17451753
file: StaticString,
17461754
line: UInt
17471755
) {
1756+
let updateStateToExpectedResult = updateStateToExpectedResult.map { original in
1757+
{ (state: inout ScopedState) in
1758+
try XCTModifyLocals.$isExhaustive.withValue(self.exhaustivity == .on) {
1759+
try original(&state)
1760+
}
1761+
}
1762+
}
1763+
17481764
guard !self.reducer.receivedActions.isEmpty else {
17491765
XCTFail(
17501766
failureMessage(),

Tests/ComposableArchitectureTests/TestStoreNonExhaustiveTests.swift

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,71 @@
684684
await store.receive(/NonExhaustiveReceive.Action.response2)
685685
}
686686

687+
func testXCTModifyExhaustive() async {
688+
struct State: Equatable {
689+
var child: Int? = 0
690+
var count = 0
691+
}
692+
enum Action: Equatable { case tap, response }
693+
let store = TestStore(
694+
initialState: State(),
695+
reducer: Reduce<State, Action> { state, action in
696+
switch action {
697+
case .tap:
698+
state.count += 1
699+
return .send(.response)
700+
case .response:
701+
state.count += 1
702+
return .none
703+
}
704+
}
705+
)
706+
707+
await store.send(.tap) { state in
708+
state.count = 1
709+
XCTExpectFailure {
710+
XCTModify(&state.child, case: /.some) { _ in }
711+
} issueMatcher: {
712+
$0.compactDescription == """
713+
XCTModify failed: expected "Int" value to be modified but it was unchanged.
714+
"""
715+
}
716+
}
717+
await store.receive(.response) { state in
718+
state.count = 2
719+
XCTExpectFailure {
720+
XCTModify(&state.child, case: /Optional.some) { _ in }
721+
} issueMatcher: {
722+
$0.compactDescription == """
723+
XCTModify failed: expected "Int" value to be modified but it was unchanged.
724+
"""
725+
}
726+
}
727+
}
728+
729+
func testXCTModifyNonExhaustive() async {
730+
enum Action { case tap, response }
731+
let store = TestStore(
732+
initialState: Optional(1),
733+
reducer: Reduce<Int?, Action> { state, action in
734+
switch action {
735+
case .tap:
736+
return .send(.response)
737+
case .response:
738+
return .none
739+
}
740+
}
741+
)
742+
store.exhaustivity = .off
743+
744+
await store.send(.tap) {
745+
XCTModify(&$0, case: /Optional.some) { _ in }
746+
}
747+
await store.receive(.response) {
748+
XCTModify(&$0, case: /Optional.some) { _ in }
749+
}
750+
}
751+
687752
// This example comes from Krzysztof Zabłocki's blog post:
688753
// https://www.merowing.info/exhaustive-testing-in-tca/
689754
func testKrzysztofExample1() {

0 commit comments

Comments
 (0)