Skip to content

Commit eef1a4d

Browse files
kvvzrd-date
andauthored
Add initial test cases (#38)
* Add initial test case * Add a negative test case * Back to Dependency Client style * Move mock to test * Update MyLibrary/Sources/ScheduleFeature/Schedule.swift Co-authored-by: Daiki Matsudate <DaiMatsud@digital.go.jp> * Remove testValue = Self() * Create struct for response --------- Co-authored-by: Daiki Matsudate <DaiMatsud@digital.go.jp>
1 parent a6c36d2 commit eef1a4d

File tree

8 files changed

+205
-20
lines changed

8 files changed

+205
-20
lines changed

App/App.xcodeproj/project.pbxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
/* End PBXCopyFilesBuildPhase section */
4545

4646
/* Begin PBXFileReference section */
47+
2435021A2BA49F29000083BB /* App.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = App.xctestplan; sourceTree = "<group>"; };
4748
D563615B2B931FF800E4F789 /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; };
4849
D563615E2B931FF800E4F789 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
4950
D56361642B931FF900E4F789 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -106,6 +107,7 @@
106107
D56361662B931FF900E4F789 /* App.entitlements */,
107108
D563616A2B931FF900E4F789 /* Info.plist */,
108109
D583D7C12B9AD2ED00B94F73 /* PrivacyInfo.xcprivacy */,
110+
2435021A2BA49F29000083BB /* App.xctestplan */,
109111
D56361672B931FF900E4F789 /* Preview Content */,
110112
);
111113
path = App;

App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,13 @@
2727
buildConfiguration = "Debug"
2828
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
2929
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
30-
shouldUseLaunchSchemeArgsEnv = "YES"
31-
shouldAutocreateTestPlan = "YES">
30+
shouldUseLaunchSchemeArgsEnv = "YES">
31+
<TestPlans>
32+
<TestPlanReference
33+
reference = "container:App/App.xctestplan"
34+
default = "YES">
35+
</TestPlanReference>
36+
</TestPlans>
3237
</TestAction>
3338
<LaunchAction
3439
buildConfiguration = "Debug"

App/App/App.xctestplan

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"configurations" : [
3+
{
4+
"id" : "235E58B3-6724-418C-907A-6211C4CC541B",
5+
"name" : "Test Scheme Action",
6+
"options" : {
7+
8+
}
9+
}
10+
],
11+
"defaultOptions" : {
12+
"codeCoverage" : false,
13+
"targetForVariableExpansion" : {
14+
"containerPath" : "container:App.xcodeproj",
15+
"identifier" : "D563615A2B931FF800E4F789",
16+
"name" : "App"
17+
}
18+
},
19+
"testTargets" : [
20+
{
21+
"target" : {
22+
"containerPath" : "container:..\/MyLibrary",
23+
"identifier" : "ScheduleFeatureTests",
24+
"name" : "ScheduleFeatureTests"
25+
}
26+
}
27+
],
28+
"version" : 1
29+
}

MyLibrary/Package.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,12 @@ let package = Package(
6868
.plugin(name: "LicenseProviderPlugin", package: "LicenseProvider")
6969
]
7070
),
71+
.testTarget(
72+
name: "ScheduleFeatureTests",
73+
dependencies: [
74+
"ScheduleFeature",
75+
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
76+
]
77+
),
7178
]
7279
)

MyLibrary/Sources/ScheduleFeature/Schedule.swift

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ public struct Schedule {
1616
var id: Self { self }
1717
}
1818

19+
public struct SchedulesResponse: Equatable {
20+
var day1: Conference
21+
var day2: Conference
22+
var workshop: Conference
23+
}
24+
1925
@ObservableState
2026
public struct State: Equatable {
2127

@@ -29,7 +35,7 @@ public struct Schedule {
2935
@Presents var destination: Destination.State?
3036

3137
public init() {
32-
try! Tips.configure([.displayFrequency(.immediate)])
38+
try? Tips.configure([.displayFrequency(.immediate)])
3339
}
3440
}
3541

@@ -38,6 +44,7 @@ public struct Schedule {
3844
case path(StackAction<Path.State, Path.Action>)
3945
case destination(PresentationAction<Destination.Action>)
4046
case view(View)
47+
case fetchResponse(Result<SchedulesResponse, Error>)
4148

4249
public enum View {
4350
case onAppear
@@ -66,10 +73,12 @@ public struct Schedule {
6673
Reduce { state, action in
6774
switch action {
6875
case .view(.onAppear):
69-
state.day1 = try! dataClient.fetchDay1()
70-
state.day2 = try! dataClient.fetchDay2()
71-
state.workshop = try! dataClient.fetchWorkshop()
72-
return .none
76+
return .send(.fetchResponse(Result {
77+
let day1 = try dataClient.fetchDay1()
78+
let day2 = try dataClient.fetchDay2()
79+
let workshop = try dataClient.fetchWorkshop()
80+
return .init(day1: day1, day2: day2, workshop: workshop)
81+
}))
7382
case let .view(.disclosureTapped(session)):
7483
guard let description = session.description, let speakers = session.speakers else {
7584
return .none
@@ -93,6 +102,17 @@ public struct Schedule {
93102
#elseif os(visionOS)
94103
return .run { _ in await openURL(url) }
95104
#endif
105+
case let .fetchResponse(.success(response)):
106+
state.day1 = response.day1
107+
state.day2 = response.day2
108+
state.workshop = response.workshop
109+
return .none
110+
case let .fetchResponse(.failure(error as DecodingError)):
111+
assertionFailure(error.localizedDescription)
112+
return .none
113+
case let .fetchResponse(.failure(error)):
114+
print(error) // TODO: replace to Logger API
115+
return .none
96116
case .binding, .path, .destination:
97117
return .none
98118
}

MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift

Lines changed: 0 additions & 13 deletions
This file was deleted.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import Foundation
2+
import SharedModels
3+
4+
extension Conference {
5+
static let mock1 = Self(
6+
id: 1,
7+
title: "conference1",
8+
date: Date(timeIntervalSince1970: 1_000),
9+
schedules: [
10+
.init(
11+
time: Date(timeIntervalSince1970: 10_000),
12+
sessions: [
13+
.mock1,
14+
.mock2,
15+
]
16+
)
17+
]
18+
)
19+
20+
static let mock2 = Self(
21+
id: 2,
22+
title: "conference2",
23+
date: Date(timeIntervalSince1970: 2_000),
24+
schedules: [
25+
.init(
26+
time: Date(timeIntervalSince1970: 20_000),
27+
sessions: [
28+
.mock1,
29+
.mock2
30+
]
31+
)
32+
]
33+
)
34+
35+
static let mock3 = Self(
36+
id: 3,
37+
title: "conference3",
38+
date: Date(timeIntervalSince1970: 3_000),
39+
schedules: [
40+
.init(
41+
time: Date(timeIntervalSince1970: 30_000),
42+
sessions: [
43+
.mock1,
44+
.mock2
45+
]
46+
)
47+
]
48+
)
49+
}
50+
51+
extension Session {
52+
static let mock1 = Self(
53+
title: "session1",
54+
speakers: [
55+
.mock1
56+
],
57+
place: "place1",
58+
description: "description1",
59+
requirements: "requirements1"
60+
)
61+
62+
static let mock2 = Self(
63+
title: "session2",
64+
speakers: [
65+
.mock2
66+
],
67+
place: "place2",
68+
description: "description2",
69+
requirements: "requirements2"
70+
)
71+
}
72+
73+
extension Speaker {
74+
static let mock1 = Self(
75+
name: "speaker1",
76+
imageName: "image1",
77+
bio: "bio1",
78+
links: [
79+
.init(
80+
name: "sns1",
81+
url: URL(string: "https://example.com/speaker1")!
82+
)
83+
]
84+
)
85+
86+
static let mock2 = Self(
87+
name: "speaker2",
88+
imageName: "image2",
89+
bio: "bio2",
90+
links: [
91+
.init(
92+
name: "sns2",
93+
url: URL(string: "https://example.com/speaker2")!
94+
)
95+
]
96+
)
97+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import ComposableArchitecture
2+
import DataClient
3+
import XCTest
4+
5+
@testable import ScheduleFeature
6+
7+
final class ScheduleTests: XCTestCase {
8+
@MainActor
9+
func testFetchData() async {
10+
let store = TestStore(initialState: Schedule.State()) {
11+
Schedule()
12+
} withDependencies: {
13+
$0[DataClient.self].fetchDay1 = { @Sendable in .mock1 }
14+
$0[DataClient.self].fetchDay2 = { @Sendable in .mock2 }
15+
$0[DataClient.self].fetchWorkshop = { @Sendable in .mock3 }
16+
}
17+
await store.send(.view(.onAppear))
18+
await store.receive(\.fetchResponse.success) {
19+
$0.day1 = .mock1
20+
$0.day2 = .mock2
21+
$0.workshop = .mock3
22+
}
23+
}
24+
25+
@MainActor
26+
func testFetchDataFailure() async {
27+
struct FetchError: Equatable, Error {}
28+
let store = TestStore(initialState: Schedule.State()) {
29+
Schedule()
30+
} withDependencies: {
31+
$0[DataClient.self].fetchDay1 = { @Sendable in throw FetchError() }
32+
$0[DataClient.self].fetchDay2 = { @Sendable in .mock2 }
33+
$0[DataClient.self].fetchWorkshop = { @Sendable in .mock3 }
34+
}
35+
await store.send(.view(.onAppear))
36+
await store.receive(\.fetchResponse.failure)
37+
}
38+
}

0 commit comments

Comments
 (0)