안녕하세요! 피피아노입니다 🎵
이번 포스팅에서는 Test-Driven Development(TDD)의 개념과 Apple의 테스트 프레임워크인 XCTest 프레임워크에 대해서 정리를 해보려고 합니다.
그럼 바로 시작하겠습니다!
테스트의 정의
- 소프트웨어의 품질, 성능, 신뢰성을 확인하는 절차
- 소프트웨어 테스트는 프로그램이 예상대로 작동하는지 확인하는 과정
- 수동 테스트는 사람이 직접 소프트웨어를 실행하고 결과를 확인하는 방식
- 자동 테스트는 테스팅 소프트웨어 도구를 사용하여 테스트를 실행하는 테스팅 방식
수동 테스트의 장단점
장점
- 사용자의 실제 행동을 반영한 테스트 가능
- 기능이 자주 변경되는 초기 개발 단계 상황에서 적합
- UI/UX등 직관적인 부분 확인 용이
단점
- 시간이 소모적이고 비용이 많이 듦
- 대규모 애플리케이션에서는 모든 기능을 수동으로 테스트하기 어려움
- 사람이 하는 일이다 보니 인적 오류의 가능성이 높음
- 테스트 생략 시 사용자가 버그를 발견할 위험 증가
자동화 테스트의 장단점
장점
- 반복 테스트 자동 수행으로 효율적
- 빠르고 정확하며 반복 실행 가능
- 회귀 오류를 쉽게 발견
- 새로운 팀원도 쉽게 테스트 실행 가능
- 장기적으로 시간과 비용 절약
단점
- 초기 설정 및 유지 보수에 시간/비용 소모가 큼
- 직관적인 UI/UX 테스트는 어려움
- 지속적인 유지 관리 필요
- 복잡한 UI 테스트에는 한계가 있음
Test-Driven Development(TDD)란?
Test-Driven Development(TDD)란 소프트웨어 개발 방법론 중 하나로 "테스트를 먼저 작성하고, 그 테스트를 통과하기 위한 최소한의 코드를 작성한 뒤, 코드를 리팩터링 하는 방식"으로 개발을 진행하는 방법입니다.
실패하는 테스트 작성 -> 테스트 통과를 위한 최소한의 코드 구현 -> 리팩토링 사이클 반복 형태로 구성이 되고 코드의 동작을 명확히 정의하고 검증 가능한 방식으로 개발이 진행됩니다.
TDD의 장점
- 소프트웨어 설계 개선: 테스트하기 쉬운 코드는 대체로 좋은 설계를 가짐
- 요구사항에 대한 깊은 이해: 테스트 작성 시 입출력을 명확히 정의해야 함
- 회귀 오류 방지: 기존 기능에 대한 테스트가 항상 실행되기 때문에 변경사항의 영향 확인 가능
XCTest란 무엇일까?
XCTest는 Apple이 제공하는 Swift용 기본 테스팅 프레임워크로, Xcode에 통합되어 있습니다. 이 프레임워크는 유닛 테스트, 성능 테스트, UI 테스트 등 다양한 테스팅 방법을 지원합니다. XCTest를 사용하면 코드의 개별 부분이 예상대로 작동하는지 확인하고, 앱의 전체 성능을 측정하며, 사용자 인터페이스가 올바르게 기능하는지 검증할 수 있습니다.
https://developer.apple.com/documentation/xctest
XCTest | Apple Developer Documentation
Create and run unit tests, performance tests, and UI tests for your Xcode project.
developer.apple.com
(최근에는 Swift Testing이라는 새로운 프레임워크도 나오긴 했지만 이번 포스팅에서는 다루지 않고 추후에 다룰 수 있도록 하겠습니다. 관심 있으신 분들을 위해 링크는 함께 첨부하겠습니다.)
https://developer.apple.com/kr/xcode/swift-testing/
Swift Testing 개요 - Xcode - Apple Developer
새로운 프레임워크인 Swift Testing에는 Swift 코드를 쉽게 테스트할 수 있게 해주는 표현적이며 직관적인 API가 포함되어 있습니다.
developer.apple.com
Xcode 단위 테스트 타겟
- 새 프로젝트 생성 시 테스트 타겟 추가 옵션 제공
- 단위 테스트와 UI 테스트를 위한 별도의 타겟 제공
- 단위 테스트: 개별 구성 요소 테스트
- UI 테스트: 애플리케이션을 블랙박스로 테스트
XCTest의 핵심 기능
XCTAssert 함수군
XCTest는 다양한 검증 함수를 제공합니다.
- XCTAssertEqual(a, b): 두 값이 같은지 검증
- XCTAssertNotEqual(a, b): 두 값이 다른지 검증
- XCTAssertTrue(expression): 표현식이 참인지 검증
- XCTAssertFalse(expression): 표현식이 거짓인지 검증
- XCTAssertNil(value): 값이 nil인지 검증
- XCTAssertNotNil(value): 값이 nil이 아닌지 검증
- XCTAssertThrowsError(expression): 표현식이 오류를 던지는지 검증
테스트 라이프사이클
XCTest는 각 테스트 케이스에 대한 생명주기 메서드를 제공합니다.
- setUp(): 각 테스트 메서드 실행 전 호출
- tearDown(): 각 테스트 메서드 실행 후 호출
- setUpWithError(): 오류 처리가 가능한 셋업 메서드
- tearDownWithError(): 오류 처리가 가능한 정리 메서드
- setUpAllFixtures(): 모든 테스트 시작 전 한 번 호출 (iOS 14+)
- tearDownAllFixtures(): 모든 테스트 완료 후 한 번 호출 (iOS 14+)
XCTest 코드로 살펴보기
XCTest를 프로젝트 파일에서 코드로도 살펴보겠습니다.
저는 이렇게 프로젝트 파일을 구성해봤습니다.
하나씩 살펴보겠습니다.
먼저 테스트 대상은 FizzBuzz.swift 파일의 코드입니다.
public func fizzBuzz(_ number: Int) -> String {
if number % 3 == 0 && number % 5 == 0 {
return "fizz-buzz"
} else if number % 3 == 0 {
return "fizz"
} else if number % 5 == 0 {
return "buzz"
} else {
return "\(number)"
}
}
fizzBuzz(_:) 함수는 숫자를 입력받고 3의 배수면 "fizz"를 5의 배수면 "buzz"를 3과 5의 공배수면 "fizz-buzz"를 그 외 숫자는 해당 숫자를 문자열로 반환합니다.
테스트 코드는 FizzBuzzTests.swift입니다.
final class FizzBuzzTests: XCTestCase {
override func setUpWithError() throws {
print("setUpWithError")
}
override func tearDownWithError() throws {
print("tearDownWithError")
}
func testFizzBuzzDivisibleBy3() throws {
let result = fizzBuzz(3)
XCTAssertEqual(result, "fizz")
}
func testFizzBuzzDivisibleBy5() throws {
let result = fizzBuzz(5)
XCTAssertEqual(result, "buzz")
}
func testFizzBuzzDivisibleBy15() throws {
let result = fizzBuzz(15)
XCTAssertEqual(result, "fizz-buzz")
}
func testFizzBuzzNotDivisibleBy3or5ReturnInput() throws {
let result = fizzBuzz(7)
XCTAssertEqual(result, "7")
}
}
테스트 코드는 XCTestCase를 상속한 클래스를 생성해서 각 경우를 함수 단위로 검증합니다.
테스트 함수 이름은 보통 test로 시작하고 XCTAssertEqual을 통해 기대값과 실제값이 같은지를 검증합니다.
setUpWithError() / tearDownWithError()는 각각 테스트 시작 전/후에 실행되는 준비 및 정리 메서드입니다.
위 프로젝트 파일 사진을 보면 함수 옆에 행의 숫자 대신 마름모 모양이 있는 것을 확인할 수 있는데 해당 버튼을 누르면 해당 함수를 테스트합니다. 그리고 테스트가 성공하면 초록색 체크로 변하게 됩니다.
※ 종종 저 마름모 모양의 테스트 버튼이 안 보이는 경우가 있는데 그럴 때는 Xcode를 종료했다가 다시 키면 다시 원래대로 돌아오니 걱정 안 하셔도 됩니다.(오류 덩어리 Xcode...)
테스트에 실패하면 이렇게 빨간색으로 X가 생깁니다.
전체 테스트를 하는 단축키는 cmd + U입니다.
비동기 테스트도 해보자!
func asyncSum(a: Int, b: Int, complete: @escaping (Int) -> Void) async {
try? await Task.sleep(nanoseconds: 200_000_000)
complete(a + b)
}
asyncSum() 함수는 비동기로 작동하며, 약 0.2초 후 두 숫자의 합을 콜백으로 반환합니다. 이때 @escaping 클로저를 통해 결과를 전달하며, async/await을 사용합니다.
해당 함수는 아래 코드처럼 테스트 해볼 수 있습니다.
func testAsyncSum() async throws {
await asyncSum(a: 3, b: 5) { result in
XCTAssertEqual(result, 8)
}
}
오늘은 여기까지 :)
감사합니다.
잘못된 내용이 있거나 더 좋은 내용 피드백은 언제나 환영합니다!
궁금하신 부분은 댓글로 질문 부탁드립니다!
'Apple > Xcode' 카테고리의 다른 글
[Xcode] Xcode 16 pod init 에러 이슈 해결법 (9) | 2024.10.11 |
---|---|
[Xcode] LLDB 명령어 살펴보기 (1) | 2024.08.05 |
[Xcode] LLDB로 디버깅 해보기 (0) | 2024.08.01 |