iOS TDD 완벽 가이드: 개념부터 실전까지
1. 들어가며
iOS 개발에서 TDD(Test-Driven Development)는 더 이상 선택이 아닌 필수 전략으로 자리 잡고 있습니다.
특히 대규모 프로젝트나 Clean Architecture, Tuist 모듈화 환경에서 테스트 주도 개발은 유지보수성과 안정성을 보장하는 핵심 도구입니다.
많은 개발자들이 “테스트 코드를 언제, 어떻게 작성해야 하는가?”라는 질문을 던집니다.
이 글에서는 TDD의 원리를 Swift/Xcode 환경에 맞게 구체적으로 설명하고,
실제 코드 예제와 함께 테스트 작성 → 실패 → 구현 → 성공 리팩토링의 흐름을 단계별로 살펴봅니다.
2. TDD란 무엇인가?
TDD(Test-Driven Development)는 테스트 코드를 먼저 작성하고,
그 테스트를 통과시키기 위한 실제 코드를 나중에 작성하는 개발 방식입니다.
핵심 프로세스는 다음 세 단계로 요약됩니다.
- Red: 실패하는 테스트 작성 (기능 명세를 코드로 표현)
- Green: 테스트를 통과시키기 위한 최소한의 코드 작성
- Refactor: 중복 제거 및 코드 개선
이 세 단계를 반복하며 점진적으로 기능을 완성합니다.
3. iOS에서 TDD를 적용하는 이유
항목설명
| 안정성 확보 | 기능 추가나 리팩토링 시 기존 코드의 안정성을 보장 |
| 자동 회귀 테스트 | UI나 네트워크 변경 시 기존 동작이 유지되는지 즉시 확인 가능 |
| 명세 기반 개발 | 테스트 코드 자체가 요구사항을 명확히 정의하는 문서 역할 |
| 리팩토링 용이성 | 테스트가 안전망 역할을 하여 구조 개선 시 부담 감소 |
4. Xcode에서의 TDD 환경 구성
Xcode는 기본적으로 XCTest 프레임워크를 지원합니다.
새 프로젝트 생성 시 “Include Tests” 옵션을 활성화하면 자동으로 Test Target이 함께 만들어집니다.
필수 구성 요소
- XCTest: 기본 단위 테스트 프레임워크
- XCUITest: UI 테스트 프레임워크
- Quick/Nimble: BDD(Behavior-Driven Development) 스타일의 테스트를 위한 오픈소스
- Tuist: 대규모 모듈 프로젝트 구성 시 자동 테스트 스킴 생성에 활용 가능
구조 예시
MyApp/
┣ Sources/
┃ ┗ Feature/
┃ ┣ LoginViewModel.swift
┃ ┗ UserService.swift
┣ Tests/
┃ ┗ FeatureTests/
┃ ┣ LoginViewModelTests.swift
┃ ┗ UserServiceTests.swift
┗ MyApp.xcodeproj
5. 기본 예제: Login 기능의 TDD 흐름
(1) 실패하는 테스트 작성 (Red 단계)
import XCTest
@testable import MyApp
final class LoginViewModelTests: XCTestCase {
func test_loginSuccess_whenCredentialsAreValid() {
// Given
let viewModel = LoginViewModel(service: MockUserService())
// When
viewModel.login(username: "admin", password: "1234")
// Then
XCTAssertTrue(viewModel.isLoggedIn)
}
}
아직 LoginViewModel이나 MockUserService가 구현되어 있지 않기 때문에 컴파일조차 되지 않습니다.
이것이 바로 “Red 단계”입니다.
(2) 최소한의 코드로 테스트 통과시키기 (Green 단계)
class LoginViewModel {
var isLoggedIn = false
private let service: UserServiceProtocol
init(service: UserServiceProtocol) {
self.service = service
}
func login(username: String, password: String) {
if service.validate(username: username, password: password) {
isLoggedIn = true
}
}
}
protocol UserServiceProtocol {
func validate(username: String, password: String) -> Bool
}
final class MockUserService: UserServiceProtocol {
func validate(username: String, password: String) -> Bool {
return username == "admin" && password == "1234"
}
}
이제 테스트를 실행하면 ✅ 통과(Green)합니다.
(3) 리팩토링 (Refactor 단계)
final class LoginViewModel {
private(set) var isLoggedIn = false
private let service: UserServiceProtocol
init(service: UserServiceProtocol) {
self.service = service
}
func login(username: String, password: String) {
isLoggedIn = service.validate(username: username, password: password)
}
}
리팩토링 후 다시 테스트를 돌려 모든 테스트가 통과하는지 확인합니다.
6. BDD 스타일 테스트 (Quick/Nimble 사용)
import Quick
import Nimble
@testable import MyApp
final class LoginViewModelSpec: QuickSpec {
override func spec() {
describe("LoginViewModel") {
var viewModel: LoginViewModel!
beforeEach {
viewModel = LoginViewModel(service: MockUserService())
}
context("when credentials are valid") {
it("logs in successfully") {
viewModel.login(username: "admin", password: "1234")
expect(viewModel.isLoggedIn).to(beTrue())
}
}
}
}
}
7. 테스트의 종류
테스트 유형설명예시
| Unit Test | 개별 로직 검증 | ViewModel, UseCase |
| Integration Test | 모듈 간 상호작용 검증 | Network + Repository |
| UI Test | 사용자 인터페이스 동작 확인 | 버튼 클릭, 화면 전환 |
| Snapshot Test | UI 시각적 변경 감지 | Diffable UI 테스트 |
| Performance Test | 성능 및 메모리 측정 | CoreData 저장 성능 |
8. iOS TDD 실무 적용 팁
- 테스트 가능한 구조로 설계하라
- 의존성 주입(DI)을 적극 활용하라
- 테스트 커버리지 목표 설정
- CI/CD와 연동
- UI Test는 최소화하라
9. Tuist 환경에서의 TDD 구성
import ProjectDescription
let workspace = Workspace(
name: "MyApp",
projects: ["Projects/**"],
generationOptions: .options(
autogeneratedWorkspaceSchemes: .enabled(codeCoverageMode: .all)
)
)
10. TDD의 장단점
장점단점
| 버그 조기 발견 | 초기 개발 속도 저하 |
| 명확한 요구사항 반영 | 테스트 유지보수 비용 |
| 리팩토링 안정성 확보 | UI 중심 기능 테스트 한계 |
| 팀 협업 시 일관성 향상 | 비즈니스 로직이 자주 바뀔 경우 수정 부담 |
11. 결론
iOS 개발에서 TDD는 단순한 테스트 기법이 아니라 “설계 방법론”입니다.
'Dev Study > iOS' 카테고리의 다른 글
| Swift 날짜 포맷(DateFormatter) 치트시트 & 사용법 정리 (iOS) (0) | 2025.11.06 |
|---|---|
| iOS 위젯에서 앱 특정 화면으로 이동하는 방법 (0) | 2025.11.06 |
| iOS 앱 크래시 발생 시 확인하는 방법 (0) | 2025.11.06 |
| iOS DocC 완전 정복 가이드: Swift 문서화를 위한 최고의 도구 (0) | 2025.11.06 |
| iOS 개발자를 위한 Tuist 완벽 가이드 (0) | 2025.11.03 |
| Mac OS 업데이트 이후 Xcode 실행 (0) | 2024.12.11 |
| XCode - Unable to process request - PLA Update available (0) | 2024.11.17 |
| 다크모드 'traitCollectionDidChange’ deprecated in iOS 17.0 (0) | 2024.03.22 |


