본문 바로가기
Swift

Swift: XCTest

by songmoro 2024. 8. 12.
728x90

XCTest란

프로젝트에 대한 단위 테스트, 성능 테스트, UI 테스트를 수행할 수 있는 프레임워크

 

테스트는 코드 실행 중 특정 조건을 충족하는지 혹은 충족하지 않는지 기록한다. 또한, 코드의 성능을 측정하거나, UI의 흐름을 검증할 수 있다.

 

 

테스트 범위

XCTest는 다양한 단계의 수준에서 테스트 코드를 작성하는데 용이하다.

좋은 테스트를 위한 전략은 아래 테스트 피라미드와 같이 여러 유형의 테스트를 결합하는 것이다.

 

테스트 유형

  1. 앱의 논리적인 부분을 다루기 위한 많은 수의 빠르고, 잘 분리된 단위 테스트(unit test)
  2. 여러 객체, 데이터 구조들의 연결을 검증하기 위한 적은 수의 통합 테스트(intergration test)
  3. 일반적인 사용 사례(use case)의 올바른 동작을 확인하기 위한 UI 테스트(ui test)
  4. 테스트 피라미드 외의 앱의 성능 향상을 위한 성능 테스트(performance test)

 

테스트 피라미드는 다음을 제공하기 위해 사용

  1. Fidelity to User Goals: 사용자가 작업을 완료할 수 있음을 보여주는 Hi-fi(high-fidelity) 테스트
  2. Precision of Feedback: 앱 로직의 정확성과 변경 사항의 영향에 대한 빠른 피드백 제공

 

 

Unit Tests

단위 테스트는 프로젝트의 메서드 혹은 함수를 통해 단일 경로에 대한 예상 동작을 확인해야 하고, 만약 여러 경로를 다루려면, 각 시나리오 당 하나의 테스트를 작성해야 한다.

예를 들어서, 함수가 옵셔널 매개 변수를 받는 다면, 매개 변수가 “nil, non-nil” 두 개의 테스트를 작성해야 함을 의미

 

코드에서 경계 케이스와 논리적인 분기를 식별하고, 이러한 케이스들의 조합을 다루기 위해 단위 테스트를 작성해야 한다.

테스트할 클래스나 함수를 선택하고, 해당 클래스나 함수에 대한 테스트를 포함하는 XCTestCase의 하위 클래스를 만들면 되며 테스트 메서드를 작성하는 방법은 아래와 같다.

 

테스트 메서드는 아래의 세 단계를 포함합니다.(Arrange-Act-Assert 패턴)

  1. Arrange: 테스트할 클래스나 함수를 사용하는 객체, 데이터 구조 생성.
    1. 빠른 테스트가 가능하도록 복잡한 종속성을 “스텁”으로 교체(프로토콜 지향 프로그래밍을 채택하면 앱의 객체 간의 관계가 스텁에 대한 실제 구현을 대체할 만큼 충분히 유연함을 보장)
  2. Act: Arrange에서 구성한 매개 변수와 속성을 사용해 테스트 중인 메서드나 함수를 호출하기
  3. Assert: XCTest 프레임워크의 Test Assertion을 사용해 Act 단계에서 수행한 테스트 코드의 결과 확인

 

class MyAPITests : XCTestCase {
    func testMyAPIWorks() {
        // Arrange: 필수 종속 생성
        // Act: Arrage에서 생성한 종속을 사용해 API 호출
        XCTAssertTrue(/* … */, "에러 메시지 출력")
    }
}

 

테스트 케이스, 테스트 메소드

테스트 케이스(test case)는 코드의 특정 부분을 테스트하는 작고 독립적인 방법이며, 관련 테스트 메서드(test method)의 그룹으로 각각 XCTestCase의 하위 클래스

 

프로젝트에 테스트를 추가하기 위한 방법 3가지

  1. 테스트할 타깃 내에서 XCTestCase의 새로운 하위 클래스 생성
  2. 테스트 케이스에 하나 이상의 테스트 메서드 추가
  3. 각 테스트 메서드에 하나 이상의 테스트 어써션(test assertion) 추가

 

테스트 메서드는 XCTestCase 하위 클래스의 인스턴스 메소드이며 매개 변수, 반환 값이 없고 lowercase test로 시작하는 이름을 갖는다.(테스트 메소드는 Xcode의 XCTest 프레임워크에 의해 자동으로 감지)

 

class TableValidationTests: XCTestCase {
    func testEmptyTableRowAndColumnCount() {
        let table = Table()
        XCTAssertEqual(table.rowCount, 0, "Row count was not zero.")
        XCTAssertEqual(table.columnCount, 0, "Column count was not zero.")
    }
}

 

위 코드는 단일 테스트 메서드인testEmptyTableRowAndColumnCount()를 사용해서 XCTestCase 하위 클래스인 TableValidationTests를 정의

이 테스트 메서드는 Table이라는 클래스의 새 인스턴스를 만들고, rowCount와 columnCount의 속성이 초기화 후 0과 같은지 확인한다.

 

테스트 메소드 내부의 조건을 확인(또는 assert)하여 코드가 예상대로 동작하는지 확인할 수 있으며, XCTAssert 함수를 사용해서 Bool 조건, nil, non-nil 값, 예상 값, throw 된 오류를 확인할 수 있다.

예를 들어, TableValidationTests에선 XCTAssertEqual(_:_:_:file:line:) 함수를 사용해서 두 정수가 같은 값을 가지고 있는지 확인한다.

 

테스트 케이스와 테스트 메서드의 이름은 Xcode의 테스트 내비게이터, 통합 보고서(intergration report)에서 테스트를 그룹화하고, 식별하는 데 사용

 

따라서 테스트 조직을 명확히 하기 위해, 각 테스트 케이스에 “TableValidationTests, NetworkReachabilityTests, JSONParsingTests”와 같이 테스트를 요약한 이름을 지정해야 한다.

또한 실패한 테스트를 식별하기 위해, 각 테스트 메서드에 “testEmptyTableRowAndColumnCount(), testUnreachableURLAccessThrowsAnError(), testUserJSONFeedParsing()”와 같이 해당 메서드가 테스트되는 것을 명확하게 하는 이름을 지정해야 한다.

 

 

테스트 방법론

출력 기반 테스트

함수의 출력을 직접 검증하는 방법.

주어진 입력에 대해 특정 출력을 예상하는 경우 사용한다.

 

class Calculator {
    func add(_ a: Int, _ b: Int) -> Int {
        return a + b
    }
}

class CalculatorTests: XCTestCase {
    func testAddition() {
        let calculator = Calculator()
        let result = calculator.add(2, 3)
        
        XCTAssertEqual(result, 5, "2 + 3은 5가 되야한다.")
    }
}

 

 

상태 기반 테스트

객체의 상태 변화를 검증하는 방법.

메소드 호출 후 내부 상태가 예상대로 변경되는지 확인하기 위해 사용한다.

 

class Counter {
    var count = 0
    
    func increment() { count += 1 }
    func reset() { count = 0 }
}

class CounterTests: XCTestCase {
    func testIncrement() {
        let counter = Counter()
        
        counter.increment()
        
        XCTAssertEqual(counter.count, 1, "increment는 count가 1이 되어야 한다.")
    }
    
    func testReset() {
        let counter = Counter()
        counter.increment()
        counter.reset()
        
        XCTAssertEqual(counter.count, 0, "reset은 count가 0이 되어야 한다.")
    }
}

 

 

 

 

728x90

'Swift' 카테고리의 다른 글

Swift: Async, Await, Thread  (0) 2024.08.12
Swift: OperationQueue  (0) 2024.08.12
Swift: 정규 표현식  (0) 2024.08.12
Swift: 객체지향 프로그래밍의 요소 예제  (0) 2024.08.12
노션 API w/ SwiftUI  (1) 2024.05.01