Skip to content

Commit ce64236

Browse files
committed
test: Firebase 에뮬레이터를 사용하여 로컬에서 테스트하는 방향으로 테스트 코드 작성
1 parent 5d95643 commit ce64236

11 files changed

Lines changed: 1060 additions & 0 deletions

File tree

DevLog.xcodeproj/project.pbxproj

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,18 @@
2020
DFF2DACE2EDC02AD00778738 /* OrderedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = DFF2DACD2EDC02AD00778738 /* OrderedCollections */; };
2121
/* End PBXBuildFile section */
2222

23+
/* Begin PBXContainerItemProxy section */
24+
DF34164B2E45F67C00F9312B /* PBXContainerItemProxy */ = {
25+
isa = PBXContainerItemProxy;
26+
containerPortal = DFD48AF82DC4D6E2005905C5 /* Project object */;
27+
proxyType = 1;
28+
remoteGlobalIDString = DFD48AFF2DC4D6E2005905C5;
29+
remoteInfo = DevLog;
30+
};
31+
/* End PBXContainerItemProxy section */
32+
2333
/* Begin PBXFileReference section */
34+
DF3416492E45F67C00F9312B /* DevLog_Unit.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DevLog_Unit.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
2435
DFD48B002DC4D6E2005905C5 /* DevLog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DevLog.app; sourceTree = BUILT_PRODUCTS_DIR; };
2536
DFD6453F2EC827A10073E133 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = "<group>"; };
2637
DFD74E2E2E423EA700613803 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
@@ -38,6 +49,11 @@
3849
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
3950

4051
/* Begin PBXFileSystemSynchronizedRootGroup section */
52+
DF34164A2E45F67C00F9312B /* DevLog_Unit */ = {
53+
isa = PBXFileSystemSynchronizedRootGroup;
54+
path = DevLog_Unit;
55+
sourceTree = "<group>";
56+
};
4157
DF8AB7982E938B0B00E50BBF /* DevLog */ = {
4258
isa = PBXFileSystemSynchronizedRootGroup;
4359
exceptions = (
@@ -49,6 +65,13 @@
4965
/* End PBXFileSystemSynchronizedRootGroup section */
5066

5167
/* Begin PBXFrameworksBuildPhase section */
68+
DF3416472E45F67C00F9312B /* Frameworks */ = {
69+
isa = PBXFrameworksBuildPhase;
70+
buildActionMask = 2147483647;
71+
files = (
72+
);
73+
runOnlyForDeploymentPostprocessing = 0;
74+
};
5275
DFD48AFD2DC4D6E2005905C5 /* Frameworks */ = {
5376
isa = PBXFrameworksBuildPhase;
5477
buildActionMask = 2147483647;
@@ -73,6 +96,7 @@
7396
children = (
7497
DFD6453F2EC827A10073E133 /* .gitignore */,
7598
DF8AB7982E938B0B00E50BBF /* DevLog */,
99+
DF34164A2E45F67C00F9312B /* DevLog_Unit */,
76100
DFD74E2E2E423EA700613803 /* README.md */,
77101
DFE28EB62DCCF26300B28FE5 /* Frameworks */,
78102
DFD48B012DC4D6E2005905C5 /* Products */,
@@ -82,6 +106,7 @@
82106
DFD48B012DC4D6E2005905C5 /* Products */ = {
83107
isa = PBXGroup;
84108
children = (
109+
DF3416492E45F67C00F9312B /* DevLog_Unit.xctest */,
85110
DFD48B002DC4D6E2005905C5 /* DevLog.app */,
86111
);
87112
name = Products;
@@ -97,6 +122,29 @@
97122
/* End PBXGroup section */
98123

99124
/* Begin PBXNativeTarget section */
125+
DF3416442E45F67C00F9312B /* DevLog_Unit */ = {
126+
isa = PBXNativeTarget;
127+
buildConfigurationList = DF3416452E45F67C00F9312B /* Build configuration list for PBXNativeTarget "DevLog_Unit" */;
128+
buildPhases = (
129+
DF3416462E45F67C00F9312B /* Sources */,
130+
DF3416472E45F67C00F9312B /* Frameworks */,
131+
DF3416482E45F67C00F9312B /* Resources */,
132+
);
133+
buildRules = (
134+
);
135+
dependencies = (
136+
DF34164C2E45F67C00F9312B /* PBXTargetDependency */,
137+
);
138+
fileSystemSynchronizedGroups = (
139+
DF34164A2E45F67C00F9312B /* DevLog_Unit */,
140+
);
141+
name = DevLog_Unit;
142+
packageProductDependencies = (
143+
);
144+
productName = DevLog_Unit;
145+
productReference = DF3416492E45F67C00F9312B /* DevLog_Unit.xctest */;
146+
productType = "com.apple.product-type.bundle.unit-test";
147+
};
100148
DFD48AFF2DC4D6E2005905C5 /* DevLog */ = {
101149
isa = PBXNativeTarget;
102150
buildConfigurationList = DFD48B112DC4D6E4005905C5 /* Build configuration list for PBXNativeTarget "DevLog" */;
@@ -139,6 +187,10 @@
139187
LastSwiftUpdateCheck = 1640;
140188
LastUpgradeCheck = 2600;
141189
TargetAttributes = {
190+
DF3416442E45F67C00F9312B = {
191+
CreatedOnToolsVersion = 16.3;
192+
TestTargetID = DFD48AFF2DC4D6E2005905C5;
193+
};
142194
DFD48AFF2DC4D6E2005905C5 = {
143195
CreatedOnToolsVersion = 16.3;
144196
};
@@ -165,12 +217,20 @@
165217
projectDirPath = "";
166218
projectRoot = "";
167219
targets = (
220+
DF3416442E45F67C00F9312B /* DevLog_Unit */,
168221
DFD48AFF2DC4D6E2005905C5 /* DevLog */,
169222
);
170223
};
171224
/* End PBXProject section */
172225

173226
/* Begin PBXResourcesBuildPhase section */
227+
DF3416482E45F67C00F9312B /* Resources */ = {
228+
isa = PBXResourcesBuildPhase;
229+
buildActionMask = 2147483647;
230+
files = (
231+
);
232+
runOnlyForDeploymentPostprocessing = 0;
233+
};
174234
DFD48AFE2DC4D6E2005905C5 /* Resources */ = {
175235
isa = PBXResourcesBuildPhase;
176236
buildActionMask = 2147483647;
@@ -183,6 +243,13 @@
183243
/* End PBXResourcesBuildPhase section */
184244

185245
/* Begin PBXSourcesBuildPhase section */
246+
DF3416462E45F67C00F9312B /* Sources */ = {
247+
isa = PBXSourcesBuildPhase;
248+
buildActionMask = 2147483647;
249+
files = (
250+
);
251+
runOnlyForDeploymentPostprocessing = 0;
252+
};
186253
DFD48AFC2DC4D6E2005905C5 /* Sources */ = {
187254
isa = PBXSourcesBuildPhase;
188255
buildActionMask = 2147483647;
@@ -193,13 +260,68 @@
193260
/* End PBXSourcesBuildPhase section */
194261

195262
/* Begin PBXTargetDependency section */
263+
DF34164C2E45F67C00F9312B /* PBXTargetDependency */ = {
264+
isa = PBXTargetDependency;
265+
target = DFD48AFF2DC4D6E2005905C5 /* DevLog */;
266+
targetProxy = DF34164B2E45F67C00F9312B /* PBXContainerItemProxy */;
267+
};
196268
DF66A07D2EA52E9F0098E643 /* PBXTargetDependency */ = {
197269
isa = PBXTargetDependency;
198270
productRef = DF66A07C2EA52E9F0098E643 /* SwiftLintBuildToolPlugin */;
199271
};
200272
/* End PBXTargetDependency section */
201273

202274
/* Begin XCBuildConfiguration section */
275+
DF34164D2E45F67C00F9312B /* Debug */ = {
276+
isa = XCBuildConfiguration;
277+
buildSettings = {
278+
BUNDLE_LOADER = "$(TEST_HOST)";
279+
CODE_SIGN_STYLE = Automatic;
280+
CURRENT_PROJECT_VERSION = 1;
281+
DEVELOPMENT_TEAM = 4CPC6N38WA;
282+
GENERATE_INFOPLIST_FILE = YES;
283+
IPHONEOS_DEPLOYMENT_TARGET = 17;
284+
MARKETING_VERSION = 1.0;
285+
PRODUCT_BUNDLE_IDENTIFIER = opfic.DevLog_Unit;
286+
PRODUCT_NAME = "$(TARGET_NAME)";
287+
STRING_CATALOG_GENERATE_SYMBOLS = NO;
288+
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
289+
SUPPORTS_MACCATALYST = NO;
290+
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
291+
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
292+
SWIFT_EMIT_LOC_STRINGS = NO;
293+
SWIFT_VERSION = 5.0;
294+
TARGETED_DEVICE_FAMILY = 1;
295+
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DevLog.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/DevLog";
296+
TEST_TARGET_NAME = DevLog;
297+
};
298+
name = Debug;
299+
};
300+
DF34164E2E45F67C00F9312B /* Release */ = {
301+
isa = XCBuildConfiguration;
302+
buildSettings = {
303+
BUNDLE_LOADER = "$(TEST_HOST)";
304+
CODE_SIGN_STYLE = Automatic;
305+
CURRENT_PROJECT_VERSION = 1;
306+
DEVELOPMENT_TEAM = 4CPC6N38WA;
307+
GENERATE_INFOPLIST_FILE = YES;
308+
IPHONEOS_DEPLOYMENT_TARGET = 17;
309+
MARKETING_VERSION = 1.0;
310+
PRODUCT_BUNDLE_IDENTIFIER = opfic.DevLog_Unit;
311+
PRODUCT_NAME = "$(TARGET_NAME)";
312+
STRING_CATALOG_GENERATE_SYMBOLS = NO;
313+
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
314+
SUPPORTS_MACCATALYST = NO;
315+
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
316+
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
317+
SWIFT_EMIT_LOC_STRINGS = NO;
318+
SWIFT_VERSION = 5.0;
319+
TARGETED_DEVICE_FAMILY = 1;
320+
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DevLog.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/DevLog";
321+
TEST_TARGET_NAME = DevLog;
322+
};
323+
name = Release;
324+
};
203325
DFD48B122DC4D6E4005905C5 /* Debug */ = {
204326
isa = XCBuildConfiguration;
205327
baseConfigurationReferenceAnchor = DF8AB7982E938B0B00E50BBF /* DevLog */;
@@ -426,6 +548,15 @@
426548
/* End XCBuildConfiguration section */
427549

428550
/* Begin XCConfigurationList section */
551+
DF3416452E45F67C00F9312B /* Build configuration list for PBXNativeTarget "DevLog_Unit" */ = {
552+
isa = XCConfigurationList;
553+
buildConfigurations = (
554+
DF34164D2E45F67C00F9312B /* Debug */,
555+
DF34164E2E45F67C00F9312B /* Release */,
556+
);
557+
defaultConfigurationIsVisible = 0;
558+
defaultConfigurationName = Release;
559+
};
429560
DFD48AFB2DC4D6E2005905C5 /* Build configuration list for PBXProject "DevLog" */ = {
430561
isa = XCConfigurationList;
431562
buildConfigurations = (

DevLog.xcodeproj/xcshareddata/xcschemes/DevLog.xcscheme

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@
2929
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
3030
shouldUseLaunchSchemeArgsEnv = "YES"
3131
shouldAutocreateTestPlan = "YES">
32+
<Testables>
33+
<TestableReference
34+
skipped = "NO"
35+
parallelizable = "YES">
36+
<BuildableReference
37+
BuildableIdentifier = "primary"
38+
BlueprintIdentifier = "DF3416442E45F67C00F9312B"
39+
BuildableName = "DevLog_Unit.xctest"
40+
BlueprintName = "DevLog_Unit"
41+
ReferencedContainer = "container:DevLog.xcodeproj">
42+
</BuildableReference>
43+
</TestableReference>
44+
</Testables>
3245
</TestAction>
3346
<LaunchAction
3447
buildConfiguration = "Debug"

DevLog.xcodeproj/xcshareddata/xcschemes/DevLog_Unit.xcscheme

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,32 @@
3737
debugDocumentVersioning = "YES"
3838
debugServiceExtension = "internal"
3939
allowLocationSimulation = "YES">
40+
<BuildableProductRunnable
41+
runnableDebuggingMode = "0">
42+
<BuildableReference
43+
BuildableIdentifier = "primary"
44+
BlueprintIdentifier = "DFD48AFF2DC4D6E2005905C5"
45+
BuildableName = "DevLog.app"
46+
BlueprintName = "DevLog"
47+
ReferencedContainer = "container:DevLog.xcodeproj">
48+
</BuildableReference>
49+
</BuildableProductRunnable>
4050
</LaunchAction>
4151
<ProfileAction
4252
buildConfiguration = "Release"
4353
shouldUseLaunchSchemeArgsEnv = "YES"
4454
savedToolIdentifier = ""
4555
useCustomWorkingDirectory = "NO"
4656
debugDocumentVersioning = "YES">
57+
<MacroExpansion>
58+
<BuildableReference
59+
BuildableIdentifier = "primary"
60+
BlueprintIdentifier = "DFD48AFF2DC4D6E2005905C5"
61+
BuildableName = "DevLog.app"
62+
BlueprintName = "DevLog"
63+
ReferencedContainer = "container:DevLog.xcodeproj">
64+
</BuildableReference>
65+
</MacroExpansion>
4766
</ProfileAction>
4867
<AnalyzeAction
4968
buildConfiguration = "Debug">
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//
2+
// PushNotificationListViewModelTests.swift
3+
// DevLog_Unit
4+
//
5+
// Created by opfic on 4/6/26.
6+
//
7+
8+
import Testing
9+
import Foundation
10+
@testable import DevLog
11+
12+
@MainActor
13+
struct DeletePushNotificationTests {
14+
@Test("삭제하면 항목이 즉시 사라지고 되돌리기 토스트가 표시되며 삭제 유스케이스가 호출된다")
15+
func 삭제하면_항목이_즉시_사라지고_되돌리기_토스트가_표시되며_삭제_유스케이스가_호출된다() async throws {
16+
let fetchPushNotificationsUseCaseSpy = FetchPushNotificationsUseCaseSpy(
17+
pushNotificationPage: PushNotificationPage(
18+
items: [
19+
PushNotification(
20+
id: "notification-1",
21+
title: "title",
22+
body: "body",
23+
receivedAt: .now,
24+
isRead: false,
25+
todoId: "todo-1",
26+
todoCategory: .system(.feature)
27+
)
28+
],
29+
nextCursor: nil
30+
)
31+
)
32+
let deletePushNotificationUseCaseSpy = DeletePushNotificationUseCaseSpy()
33+
let undoDeletePushNotificationUseCaseSpy = UndoDeletePushNotificationUseCaseSpy()
34+
let togglePushNotificationReadUseCaseSpy = TogglePushNotificationReadUseCaseSpy()
35+
let fetchPushNotificationQueryUseCaseSpy = FetchPushNotificationQueryUseCaseSpy()
36+
let updatePushNotificationQueryUseCaseSpy = UpdatePushNotificationQueryUseCaseSpy()
37+
38+
let pushNotificationListViewModel = PushNotificationListViewModel(
39+
fetchUseCase: fetchPushNotificationsUseCaseSpy,
40+
deleteUseCase: deletePushNotificationUseCaseSpy,
41+
undoDeleteUseCase: undoDeletePushNotificationUseCaseSpy,
42+
toggleReadUseCase: togglePushNotificationReadUseCaseSpy,
43+
fetchQueryUseCase: fetchPushNotificationQueryUseCaseSpy,
44+
updateQueryUseCase: updatePushNotificationQueryUseCaseSpy
45+
)
46+
47+
pushNotificationListViewModel.send(.fetchNotifications)
48+
await waitUntil {
49+
!pushNotificationListViewModel.state.notifications.isEmpty
50+
}
51+
52+
let pushNotificationItem = try #require(pushNotificationListViewModel.state.notifications.first)
53+
54+
pushNotificationListViewModel.send(.deleteNotification(pushNotificationItem))
55+
56+
#expect(pushNotificationListViewModel.state.notifications.isEmpty)
57+
#expect(pushNotificationListViewModel.state.showToast)
58+
59+
await waitUntil {
60+
deletePushNotificationUseCaseSpy.calledNotificationIds == ["notification-1"]
61+
}
62+
63+
#expect(deletePushNotificationUseCaseSpy.calledNotificationIds == ["notification-1"])
64+
}
65+
66+
@Test("삭제를 되돌리면 되돌리기 유스케이스가 호출되고 다시 조회한다")
67+
func 삭제를_되돌리면_되돌리기_유스케이스가_호출되고_다시_조회한다() async throws {
68+
let fetchPushNotificationsUseCaseSpy = FetchPushNotificationsUseCaseSpy(
69+
pushNotificationPage: PushNotificationPage(
70+
items: [
71+
PushNotification(
72+
id: "notification-1",
73+
title: "title",
74+
body: "body",
75+
receivedAt: .now,
76+
isRead: false,
77+
todoId: "todo-1",
78+
todoCategory: .system(.feature)
79+
)
80+
],
81+
nextCursor: nil
82+
)
83+
)
84+
let deletePushNotificationUseCaseSpy = DeletePushNotificationUseCaseSpy()
85+
let undoDeletePushNotificationUseCaseSpy = UndoDeletePushNotificationUseCaseSpy()
86+
let togglePushNotificationReadUseCaseSpy = TogglePushNotificationReadUseCaseSpy()
87+
let fetchPushNotificationQueryUseCaseSpy = FetchPushNotificationQueryUseCaseSpy()
88+
let updatePushNotificationQueryUseCaseSpy = UpdatePushNotificationQueryUseCaseSpy()
89+
90+
let pushNotificationListViewModel = PushNotificationListViewModel(
91+
fetchUseCase: fetchPushNotificationsUseCaseSpy,
92+
deleteUseCase: deletePushNotificationUseCaseSpy,
93+
undoDeleteUseCase: undoDeletePushNotificationUseCaseSpy,
94+
toggleReadUseCase: togglePushNotificationReadUseCaseSpy,
95+
fetchQueryUseCase: fetchPushNotificationQueryUseCaseSpy,
96+
updateQueryUseCase: updatePushNotificationQueryUseCaseSpy
97+
)
98+
99+
pushNotificationListViewModel.send(.fetchNotifications)
100+
await waitUntil {
101+
!pushNotificationListViewModel.state.notifications.isEmpty
102+
}
103+
104+
let pushNotificationItem = try #require(pushNotificationListViewModel.state.notifications.first)
105+
106+
pushNotificationListViewModel.send(.deleteNotification(pushNotificationItem))
107+
pushNotificationListViewModel.send(.undoDelete)
108+
109+
await waitUntil {
110+
undoDeletePushNotificationUseCaseSpy.calledNotificationIds == ["notification-1"]
111+
}
112+
113+
#expect(undoDeletePushNotificationUseCaseSpy.calledNotificationIds == ["notification-1"])
114+
#expect(2 <= fetchPushNotificationsUseCaseSpy.executeCallCount)
115+
}
116+
}

0 commit comments

Comments
 (0)