로그인 테스트를 할 때마다 빌드를 해야는 상황으로 인해 개발 시간이 지체되는 문제가 있었습니다.

특히, UI 테스트를 진행할 때 테스트 코드의 필요성을 느끼게 됐습니다.

그래서 WWDC 15 영상과 여러 예시 코드를 참고해서 UI Test 코드를 작성할 수 있었고, 테스트 자동화로 테스트 시간을 줄일 수 있었고, 다양한 케이스를 단위 테스트할 수 있어서 안정성도 높일 수 있다는 것을 알았습니다.

UI Test 작성 및 실행 방법

  1. 각 컴포넌트의 AccessibilityIdentifier 설정 → Test 파일에서 해당 컴포넌트에 접근하기 위함
  2. 각 시나리오 별 함수 구현
  3. 함수에 성공 조건에 대한 assert 설정

샘플 코드

//  SignUpRootViewController.swift

import UIKit

final class SignUpRootViewController: TFBaseViewController {
	private lazy var startPhoneBtn = TFLoginButton(btnType: .phone)
	private lazy var startKakaoButton = TFLoginButton(btnType: .kakao)
	private lazy var startGoogleBtn = TFLoginButton(btnType: .google)
	private lazy var startNaverBtn = TFLoginButton(btnType: .naver)
	
	{ ... }

	override func viewDidLoad() {
		super.viewDidLoad()
		
		{ ... }
	}

	{ ... }
}

extension SignUpRootViewController {
	// 각 컴포넌트의 id 설정
  **private func setupAccessibilityIdentifier() {
    startPhoneBtn.accessibilityIdentifier = AccessibilityIdentifier.phoneBtn
    startKakaoButton.accessibilityIdentifier = AccessibilityIdentifier.kakoBtn
    startNaverBtn.accessibilityIdentifier = AccessibilityIdentifier.naverBtn
    startGoogleBtn.accessibilityIdentifier = AccessibilityIdentifier.googleBtn
  }**
}

//  AccessibilityIdentifier.swift
struct AccessibilityIdentifier {
  // SignUpVC
  static let phoneBtn = "phoneBtn"
  static let kakoBtn = "kakoBtn"
  static let naverBtn = "naverBtn"
  static let googleBtn = "googleBtn"
  
  // PhoneCertificationVC
  static let phoneNumberTextField = "phoneNumberTextField"
  static let verifyBtn = "verifyBtn"
}
// LoginTest.swift

import XCTest
@testable import Falling

final class FallingUITests: XCTestCase {
  var app: XCUIApplication!
  var phoneBtn: XCUIElement!
  var kakoBtn: XCUIElement!
  var naverBtn: XCUIElement!
  var googleBtn: XCUIElement!
  var phoneNumberTextField: XCUIElement!
  var verifyBtn: XCUIElement!
  
  override func setUpWithError() throws {
    app = XCUIApplication()
    phoneBtn = app.buttons[AccessibilityIdentifier.phoneBtn]
    kakoBtn = app.buttons[AccessibilityIdentifier.kakoBtn]
    naverBtn = app.buttons[AccessibilityIdentifier.naverBtn]
    googleBtn = app.buttons[AccessibilityIdentifier.googleBtn]
    phoneNumberTextField = app.textFields[AccessibilityIdentifier.phoneNumberTextField]
    verifyBtn = app.buttons[AccessibilityIdentifier.verifyBtn]
    
    Keychain.shared.clear()
    continueAfterFailure = false
    
    app.launch()
  }
  
  override func tearDownWithError() throws {
    app = nil
    phoneBtn = nil
    kakoBtn = nil
    naverBtn = nil
    googleBtn = nil
  }
  
  func testPhoneNumberValidateSuccess() throws {
    let input = "01012345678"
    
    phoneBtn.tap()
    
    phoneNumberTextField.tap()

    input.forEach { app.keys[String($0)].tap() }
    
		**XCTAssertTrue(loginButton.isEnabled)**
  }
	
	func testPhoneNumberValidateSuccess() throws {
    let input = "01012345678910"
    
    phoneBtn.tap()
    
    phoneNumberTextField.tap()

    input.forEach { app.keys[String($0)].tap() }	    

		**XCTAssertFalse(loginButton.isEnabled)**
  }
}

실무에서 구현을 하다보면 테스트 코드를 작성하지 않고 구현에 집중하는 경우가 많았는데, TDD를 적용해서 개발하면 오히려 개발 안정성과 생산성을 올릴 수 있겠다는 생각이 들었습니다.