diff --git a/Baemin/Baemin.xcodeproj/project.pbxproj b/Baemin/Baemin.xcodeproj/project.pbxproj index 9bcc197..dbbae8c 100644 --- a/Baemin/Baemin.xcodeproj/project.pbxproj +++ b/Baemin/Baemin.xcodeproj/project.pbxproj @@ -11,7 +11,18 @@ A424C26F2EAE8F72006E0F1E /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = A424C26E2EAE8F72006E0F1E /* Then */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + A40142462EC8D45F00D174C4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A4A640ED2EAB492200077FCA /* Project object */; + proxyType = 1; + remoteGlobalIDString = A4A640F42EAB492200077FCA; + remoteInfo = Baemin; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ + A40142422EC8D45F00D174C4 /* BaeminTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BaeminTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; A4A640F52EAB492200077FCA /* Baemin.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Baemin.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -26,6 +37,11 @@ /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ + A40142432EC8D45F00D174C4 /* BaeminTests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = BaeminTests; + sourceTree = ""; + }; A4A640F72EAB492200077FCA /* Baemin */ = { isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( @@ -37,6 +53,13 @@ /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ + A401423F2EC8D45F00D174C4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; A4A640F22EAB492200077FCA /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -53,6 +76,7 @@ isa = PBXGroup; children = ( A4A640F72EAB492200077FCA /* Baemin */, + A40142432EC8D45F00D174C4 /* BaeminTests */, A4A640F62EAB492200077FCA /* Products */, ); sourceTree = ""; @@ -61,6 +85,7 @@ isa = PBXGroup; children = ( A4A640F52EAB492200077FCA /* Baemin.app */, + A40142422EC8D45F00D174C4 /* BaeminTests.xctest */, ); name = Products; sourceTree = ""; @@ -68,6 +93,29 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + A40142412EC8D45F00D174C4 /* BaeminTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = A40142482EC8D45F00D174C4 /* Build configuration list for PBXNativeTarget "BaeminTests" */; + buildPhases = ( + A401423E2EC8D45F00D174C4 /* Sources */, + A401423F2EC8D45F00D174C4 /* Frameworks */, + A40142402EC8D45F00D174C4 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + A40142472EC8D45F00D174C4 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + A40142432EC8D45F00D174C4 /* BaeminTests */, + ); + name = BaeminTests; + packageProductDependencies = ( + ); + productName = BaeminTests; + productReference = A40142422EC8D45F00D174C4 /* BaeminTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; A4A640F42EAB492200077FCA /* Baemin */ = { isa = PBXNativeTarget; buildConfigurationList = A4A641082EAB492300077FCA /* Build configuration list for PBXNativeTarget "Baemin" */; @@ -102,6 +150,10 @@ LastSwiftUpdateCheck = 2600; LastUpgradeCheck = 2600; TargetAttributes = { + A40142412EC8D45F00D174C4 = { + CreatedOnToolsVersion = 26.0.1; + TestTargetID = A4A640F42EAB492200077FCA; + }; A4A640F42EAB492200077FCA = { CreatedOnToolsVersion = 26.0.1; }; @@ -126,11 +178,19 @@ projectRoot = ""; targets = ( A4A640F42EAB492200077FCA /* Baemin */, + A40142412EC8D45F00D174C4 /* BaeminTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + A40142402EC8D45F00D174C4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; A4A640F32EAB492200077FCA /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -141,6 +201,13 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + A401423E2EC8D45F00D174C4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; A4A640F12EAB492200077FCA /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -150,7 +217,69 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + A40142472EC8D45F00D174C4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = A4A640F42EAB492200077FCA /* Baemin */; + targetProxy = A40142462EC8D45F00D174C4 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ + A40142492EC8D45F00D174C4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 6B47X24XTW; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.0; + MACOSX_DEPLOYMENT_TARGET = 26.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.BaeminTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + STRING_CATALOG_GENERATE_SYMBOLS = NO; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Baemin.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Baemin"; + XROS_DEPLOYMENT_TARGET = 26.0; + }; + name = Debug; + }; + A401424A2EC8D45F00D174C4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 6B47X24XTW; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.0; + MACOSX_DEPLOYMENT_TARGET = 26.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.BaeminTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + STRING_CATALOG_GENERATE_SYMBOLS = NO; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Baemin.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Baemin"; + XROS_DEPLOYMENT_TARGET = 26.0; + }; + name = Release; + }; A4A641092EAB492300077FCA /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -349,6 +478,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + A40142482EC8D45F00D174C4 /* Build configuration list for PBXNativeTarget "BaeminTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A40142492EC8D45F00D174C4 /* Debug */, + A401424A2EC8D45F00D174C4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; A4A640F02EAB492200077FCA /* Build configuration list for PBXProject "Baemin" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Baemin/Baemin.xcodeproj/xcshareddata/xcschemes/Baemin.xcscheme b/Baemin/Baemin.xcodeproj/xcshareddata/xcschemes/Baemin.xcscheme new file mode 100644 index 0000000..3de8f5a --- /dev/null +++ b/Baemin/Baemin.xcodeproj/xcshareddata/xcschemes/Baemin.xcscheme @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Baemin/Baemin/Presentation/Login/LoginViewController.swift b/Baemin/Baemin/Presentation/Login/LoginViewController.swift index 7fcc502..77ecfa4 100644 --- a/Baemin/Baemin/Presentation/Login/LoginViewController.swift +++ b/Baemin/Baemin/Presentation/Login/LoginViewController.swift @@ -16,19 +16,19 @@ final class LoginViewController: BaseViewController { private let navigationBar = NavigationBar() - private let emailField = LoginTextField( + lazy var emailField = LoginTextField( type: .id, labelText: "이메일 아이디", placeholderText: "이메일을 입력해주세요" ) - private let passwordField = LoginTextField( + lazy var passwordField = LoginTextField( type: .password, labelText: "비밀번호", placeholderText: "비밀번호를 입력해주세요" ) - private let loginButton = CTAButton(title: "로그인", isActive: false, size: .large) + lazy var loginButton = CTAButton(title: "로그인", isActive: false, size: .large) private let findAccountButton = UIButton(type: .system).then { var config = UIButton.Configuration.plain() diff --git a/Baemin/BaeminTests/BaeminTests.swift b/Baemin/BaeminTests/BaeminTests.swift new file mode 100644 index 0000000..0a860b8 --- /dev/null +++ b/Baemin/BaeminTests/BaeminTests.swift @@ -0,0 +1,35 @@ +// +// BaeminTests.swift +// BaeminTests +// +// Created by sun on 11/16/25. +// + +import XCTest + +final class BaeminTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/Baemin/BaeminTests/LoginViewControllerTests.swift b/Baemin/BaeminTests/LoginViewControllerTests.swift new file mode 100644 index 0000000..e7a5d1d --- /dev/null +++ b/Baemin/BaeminTests/LoginViewControllerTests.swift @@ -0,0 +1,69 @@ +// +// LoginViewControllerTests.swift +// BaeminTests +// +// Created by sun on 11/16/25. +// + +import XCTest +@testable import Baemin + +final class LoginViewControllerTests: XCTestCase { + + // MARK: - Properties + + private var sut: LoginViewController! + private var navigationController: UINavigationController! + + // MARK: - Lifecycle + + override func setUp() { + super.setUp() + sut = LoginViewController() + navigationController = UINavigationController(rootViewController: sut) + + sut.loadViewIfNeeded() + } + + override func tearDown() { + navigationController = nil + sut = nil + super.tearDown() + } + + // MARK: - Tests + + func test_login_shouldNotPush_whenEmailIsInvalid() { + // Given + sut.emailField.setText("not-an-email") + sut.passwordField.setText("ValidPass1!") + XCTAssertEqual(navigationController.viewControllers.count, 1) + + // When + sut.loginButton.sendActions(for: .touchUpInside) + + // Then + XCTAssertEqual( + navigationController.viewControllers.count, + 1, + "이메일 형식이 잘못된 경우에는 화면 전환이 일어나면 안 됩니다." + ) + } + + func test_login_shouldNotPush_whenPasswordIsInvalid() { + // Given + sut.emailField.setText("test@example.com") + sut.passwordField.setText("123") + XCTAssertEqual(navigationController.viewControllers.count, 1) + + // When + sut.loginButton.sendActions(for: .touchUpInside) + + // Then + XCTAssertEqual( + navigationController.viewControllers.count, + 1, + "비밀번호 형식이 잘못된 경우에는 화면 전환이 일어나면 안 됩니다." + ) + } +}