Search
Duplicate

TDD와 XCode에서 XCTest 활용하기

생성일
2024/09/09 08:32
태그
틈틈히 자기개발
TDD

TDD와 XCode에서 XCTest 활용하기

TDD의 개념

테스트 주도 개발(Test Driven Development)은 매우 짧은 개발 사이클을 반복하는 소프트웨어 개발 프로세스 중 하나이다.
개발자는 새로운 함수를 정의하는 자동화된 Test Case를 먼저 작성한다.
이후에 Test Case를 통과하기 위한 최소한의 양의 코드를 생성한다.
그리고 마지막에 해당 코드들을 표준에 맞도록 리팩토링 하는 것이 바로 TDD 개발 방법이다.
기존의 개발의 프로세스들은 설계를 한 뒤, 코드를 작성하고 이후에 테스트를 진행하고 나서 다시 설계를 수정하는 방식이었다면,
TDD 프로세스의 경우에는, 설계를 한 뒤 테스트 코드를 작성하고 테스트 하기 때문에 설계의 문제로 인한 오류 개선 속도가 기존 개발 프로세스보다 빠르다고 할 수 있다.

TDD의 3가지 절차

실패: Writing a Failing Test

TDD 개발에서의 첫 번째 단계는 실패이다.
즉, 실패하는 테스트 케이스를 작성하는 것이다.
이때 Test Case는 프로젝트 전체의 모든 기능에 대해서 모든 케이스를 작성하는 것이 아니라,
먼저 구현할 기능에 대해서 Test Case를 하나씩 작성해 나가는 것이다.

성공: Write just enough code to pass it

두 번째 단계는 성공이다.
실패 단계에서 작성한 테스트 케이스를 통과시키기 위한 최소한의 코드를 작성하는 단계이다.

리팩토링: Change code for better without changing the behavior

세 번째 단계는 리팩토링이다.
성공 단계에서 작성한 코드에 중복되는 코드나, 개선시킬 방법이 있다면 리팩토리을 진행해준다.
리팩토링을 진행하면서 매 단계마다 Test Case를 통과하는지 확인 해야한다.
이 단계까지 구현이 완료 되었다면 다시 첫 번째 단계로 새로운 실패하는 Test Case를 작성하면 된다.

TDD의 장점

객체 지향적인 코드 개발

TDD 프로세스를 따르는 코드의 경우에는 재사용성을 고려하며 작성하게 된다.

설계 수정 시간의 단축

기존 개발 프로세스와의 TDD 프로세스를 비교한 그림에서 언급 했듯이 테스트 코드를 먼저 작성하고
바로 테스트를 해보기 때문에 설계의 구조적인 문제를 바로 찾아낼 수 있게 된다.
그리고 빠르게 피드백을 주고 받으며 수정을 하고, 추가로 다시 테스트를 진행하므로 개발이 완료된 시점에서
설계의 문제점을 발견하고 설계를 수정한 뒤, 코드 전체를 수정하지 않아도 된다.

디버깅 시간의 단축

TDD 프로세스를 따른다면 각각의 단위 테스트를 진행하기 때문에 영역을 분할하여 쉽게 벼그를 찾아낼 수 있다.

유지보수의 용이성

하지만 TDD 프로세스를 따르는 경우에는 자동화된 유닛 테스팅을 전재하므로
이러한 테스트 기간 역시 줄어들며, 기능을 수정하거나 추가할 때의 부담을 줄일 수 있다.

테스트 문서의 대체 가능

TDD 프로세스를 따르는 경우에는, 테스팅을 자동화 시킴과 동시에 정확한 테스트의 근거를 산출할 수 있다.

XCode에서 XCTest 사용하기

Xcode에서 Unit Test 프로젝트 만들기

처음에 프로젝트에서 테스트 세팅을 안했더라도, 가능하다.
// // Busanz_SwiftUITests.swift // Busanz-SwiftUITests // // Created by Hyungjun KIM on 9/10/24. // import XCTest final class Busanz_SwiftUITests: 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. } } }
Swift
복사
Busanz_SwiftUITests.swift
// // Busanz_SwiftUIUITests.swift // Busanz-SwiftUIUITests // // Created by Hyungjun KIM on 9/10/24. // import XCTest final class Busanz_SwiftUIUITests: XCTestCase { override func setUpWithError() throws { // Put setup code here. This method is called before the invocation of each test method in the class. // In UI tests it is usually best to stop immediately when a failure occurs. continueAfterFailure = false // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. } 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 { // UI tests must launch the application that they test. let app = XCUIApplication() app.launch() // Use XCTAssert and related functions to verify your tests produce the correct results. } func testLaunchPerformance() throws { if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { // This measures how long it takes to launch your application. measure(metrics: [XCTApplicationLaunchMetric()]) { XCUIApplication().launch() } } } }
Swift
복사
Busanz_SwiftUIUITests.swift
// // Busanz_SwiftUIUITestsLaunchTests.swift // Busanz-SwiftUIUITests // // Created by Hyungjun KIM on 9/10/24. // import XCTest final class Busanz_SwiftUIUITestsLaunchTests: XCTestCase { override class var runsForEachTargetApplicationUIConfiguration: Bool { true } override func setUpWithError() throws { continueAfterFailure = false } func testLaunch() throws { let app = XCUIApplication() app.launch() // Insert steps here to perform after app launch but before taking a screenshot, // such as logging into a test account or navigating somewhere in the app let attachment = XCTAttachment(screenshot: app.screenshot()) attachment.name = "Launch Screen" attachment.lifetime = .keepAlways add(attachment) } }
Swift
복사
Busanz_SwiftUIUITestsLaunchTests.swift

Unit Test 클래스

XCTest 를 import 하고 Busanz_SwiftUITestsXCTestCase 의 하위 클래스로 정의하며,
setUp() , tearDown() 과 같은 테스트 메서드를 정의한다.
이때 테스트 클래스를 실행하는데 3가지 방법이 있다.
1.
Product -> Text 또는 Command + U 를 사용해서 모든 테스트 클래스를 실행한다.
2.
테스트 네비게이터에서 화살표 버튼을 클릭한다.
3.
거터(gutter)에서 다이아몬드 버튼을 클릭한다.
모든 테스트가 성공하면, 다이아몬드는 녹색으로 바뀌게 되고, 체크 표시가 나타난다.
여기에서 끝에 이는 회색 다이아몬드를 클릭하면 성능 결과를 확인해 볼 수 있다.

Reference