iyOmSd/Title: Swift

[Swift] WWDC24 Meet Swift Testing

냄수 2024. 11. 14. 20:46
반응형

Meet Swift Testing 세션을 정리한 글입니다.

 

Swift Testing은 새로운 오픈 소스 패키지로 Swift를 사용해 코드를 테스트할 수 있습니다

Swift Testing은 Swift용으로 만들어져 동시성 및 매크로와 같은 최신 기능을 도입했습니다 Linux와 Windows 등 모든 주요 플랫폼을 지원합니다

 

Building blocks

@Test 속성이 첫 번째 구성 요소입니다

함수가 테스트임을 나타내죠 이것을 추가하면 Xcode가 인식하고 옆에 Run 버튼을 표시합니다

테스트 함수는 @Test 속성을 가진 일반적인 Swift 함수입니다

전역 함수일 수도 있고 유형의 메서드일 수도 있습니다

비동기 또는 예외로 표시하거나 필요한 경우 전역 액터로 격리할 수 있습니다

 

@testable 속성을 사용해서 테스트에 필요한 모듈을 가져올 수 있습니다.

 

#expect 매크로가 Swift Testing의 두 번째 구성 요소입니다

#expect 매크로와 같은 기대치를 사용하여 예상 조건이 true인지 확인할 수 있습니다 

이 매크로는 일반 표현식과 언어 연산자를 허용합니다 

또한 실패할 경우 소스 코드와 하위 표현식의 값을 캡처합니다 

 

결과 보기의 세부 정보에는 #expect 매크로에 전달된 표현식에 대한 세부 정보가 하위값과 함께 표시됩니다

메타데이터를 펼치면 해당 속성을 비교할 수 있습니다

 

 #expect 매크로는 매우 유연합니다 

연산자나 메서드 호출을 포함한 모든 표현식을 전달할 수 있으며 실패할 경우 자세한 결과를 보여 줍니다 

몇 가지 예를 들겠습니다 == 연산자를 사용할 수 있으며 실패 시 왼쪽과 오른쪽이 캡처되어 표시됩니다 

.isEmpty와 같은 속성에 액세스할 수 있고 

배열에서 .contains와 같은 메서드를 호출할 수도 있습니다 

오류를 잘 보시면 숫자 배열의 내용이 자동으로 표시되어 있습니다 

이 모든 작업을 하기 위해 전문적인 API를 배우지 않아도 됩니다 #expect 매크로만 사용하면 됩니다

 

 

테스트에서 예상한 결과가 나오지 않아 조기 종료하고 싶을 때가 있습니다

이 경우 #require 매크로를 사용할 수 있습니다

요구되는 기대치는 일반적인 기대치와 유사합니다

하지만 try 키워드가 있고 표현식이 false이면 오류가 발생해 테스트가 실패하고 더 이상 진행되지 않습니다

 

 #require 매크로를 사용할 수 있는 또 다른 방법은 옵셔널 값을 안전하게 언래핑하고 값이 nil인 경우 테스트를 중지하는 것입니다

 

 

이 테스트를 프로젝트에 커밋하기 전에 목적을 더 명확히 해 봅시다

@Test 속성에 맞춤화된 표시 이름을 전달하면 됩니다 

그러면 해당 이름이 테스트 내비게이터와 Xcode의 다른 위치에 표시됩니다

표시 이름은 세 번째 구성 요소인 특성의 예입니다

 

특성은 여러 가지를 할 수 있습니다

- 테스트에 대한 설명 정보를 추가하거나

- 테스트 실행 시기 또는 실행 여부를 맞춤화하거나

- 테스트 동작 방식을 수정할 수 있습니다

 

표시 이름과 함께 정보를 추가하는 것 외에도 

- 관련 버그를 참고하거나 맞춤화 태그를 추가할 수도 있습니다 

.bug, .tags

 

- 특정 조건에서만 테스트를 실행하려면 특성을 사용해 제어할 수 있습니다

.enabled, .disabled, available

 

- 일부 특성은 테스트의 실제 작동 방식에 영향을 줍니다

시간 제한을 설정하거나 한 번에 하나씩 실행하는 등으로요

.timeLimit, .serialized

 

 

두 테스트를 함께 그룹화하면 프로젝트에서 더 쉽게 찾을 수 있을 것입니다 

그렇게 하려면 VideoTests라는 구조체로 래핑하면 됩니다

이렇게 테스트가 포함된 타입을 test suite(테스트모음)라고 하며 

네 번째이자 마지막 구성 요소입니다

 

관련 테스트 함수 또는 다른 suite 그룹화에 사용됩니다 

suite은 @Suite 속성으로 명시적으로 주석을 달 수 있습니다

테스트 함수 또는 @Suites를 포함하는 모든 유형은 암시적으로 @Suite 자체로 간주함

suite은 저장된 인스턴스 속성을 가질 수 있습니다

각 테스트 전후에 init 또는 deinit으로 로직을 수행할 수 있습니다

그리고 별도의 @Suite 인스턴스가 모든 인스턴스 @Test 함수에 대해 별도로 생성되어 의도치 않은 상태 공유를 방지합니다

 

정리하면

테스트 함수는 비동기/대기 및 액터 격리를 지원하여 Swift 동시성과 원활하게 통합됩니다

expectation도 비동기/대기를 사용할 수 있으며 모든 내장된 언어 연산자를 사용할 수 있습니다

expectation와 Trait 모두 Swift 매크로를 활용하므로 자세한 실패 결과를 볼 수 있으며 코드에서 직접 테스트별 정보를 지정할 수 있습니다

suite은 값의 의미론을 채택해 상태를 분리하는 데 구조체 사용을 권장합니다

 

 

Common workflows

테스트 실행 시기를 제어

공통점이 있는 테스트 연결

매번 다른인수로 테스트를 두번이상 반복하는 작업

에 대해 알아보자

 

조건이 있는 테스트(테스트 실행 시기 제어)

일부 테스트는 특정 기기나 환경 등 특정 상황에서만 실행되어야 합니다

이러한 경우 조건 특성인 .enabled(if:...) 등을 적용할 수 있습니다

테스트가 실행되기 전에 평가할 조건을 전달하고 조건이 false이면 테스트가 건너뛴 것으로 표시됩니다

 

테스트가 절대 실행되지 않도록 하고 싶을 수도 있습니다 이 경우 .disabled(...) 특성을 사용할 수 있습니다 

테스트 비활성화는 테스트 함수를 주석 처리하는 등의 다른 기법보다 선호되는데 컴파일된 테스트 내부의 코드를 확인하기 때문입니다

.disabled(...) 특성은 테스트가 비활성화된 이유를 설명하는 데 사용할 수 있는 주석을 허용합니다 

그리고 주석은 항상 구조화된 결과에 표시되므로 CI 시스템에서 가시성을 위해 표시할 수 있습니다

 

테스트의 전체 본문을 특정 OS 버전에서만 실행할 수 있는 경우 해당 테스트에 @available(...) 속성을 배치하여 실행할 버전을 제어할 수 있습니다

런타임에서 확인할 때 #available을 사용하는 대신 @available(...) 속성을 사용하세요 

@available(...) 속성을 사용하면 테스트 라이브러리가 테스트에 OS 버전 조건이 있다는 것을 알 수 있어 이를 결과에 더 정확하게 반영할 수 있습니다

 

공통점이 있는 테스트 연결

테스트 내비게이터 하단에 모든 태그가 표시됩니다

서식 지정 태그를 @Suite로 위로 올려 여기 포함된 모든 테스트에서 상속되도록 할 수 있습니다

태그 자체는 다른 파일, suite 또는 target 테스트에 적용할 수 있으며 여러 프로젝트에서 공유할 수도 있습니다

Swift Testing을 사용할 때는 테스트 계획에 특정한 이름보다 태그를 포함시키거나 제외하는 것이 더 좋습니다

최상의 결과를 얻으려면 항상 각 상황에 가장 적합한 유형의 특성을 사용하세요 

모든 시나리오가 태그를 사용할 필요는 없습니다 

예를 들어 런타임 조건을 표현하려는 경우 앞서 언급한 대로 .enabled(if ...)를 사용하세요

 

매번 다른 인수를 사용하여 테스트 반복

위와같은 테스트코드의 문제는 매우 반복적이며 중복되는 코드 때문에 유지관리가 더 어려워짐 

각 함수에 이름 지정시 읽기도어려움

 

이 모든것을 테스트 하나로 작성 할 수 있음

 

parameterized(매개변수화된) 테스트 기능 사용

 

첫 번째 단계는 서명에 매개변수를 추가하는 것입니다

그후 테스트에 전달할 인수를 지정

원하는 경우 인자 앞에 표시 이름이나 다른 특성을 전달하여 이름을 지정할 수 있습니다

 

매개변수화된 테스트는 개념적으로 for...in 루프를 사용하여 여러 번 반복되는 단일 테스트와 유사합니다

예시를 보겠습니다

 videoNames의 배열이 있으며 테스트를 수행하기 위해 반복됩니다

그러나 이렇게 for...in 루프를 사용하면 몇 가지 단점이 있습니다

 

이 패턴을 사용하는 테스트가 보일 때마다 매개변수화된 테스트 함수로 변환하는 것이 가장 좋습니다

 함수에 매개변수를 추가하고
for...in 루프를 제거한 다음
인수를 @Test 속성으로 위로 이동하기만 하면됩니다.

 

매개변수화된 테스트를 사용하면 각 개별 인수의 세부 사항을 결과에서 명확하게 확인할 수 있습니다

세분화된 디버깅을 위해 인수를 독립적으로 재실행할 수 있습니다

또한 각 인수를 병렬로 실행하여 더 효율적으로 실행할 수 있으므로 결과를 더 빨리 얻을 수 있습니다

매개변수화된 테스트는 여기서 살펴본 것보다 훨씬 더 발전된 방식으로 사용하여 두 입력 세트의 모든 조합을 테스트하는 등의 작업이 가능합니다

 

 

 

Swift Testing and XCTest

테스트를 어떻게 마이그레이션하는지 궁금하실 것입니다 

Swift Testing은 XCTest와 비슷한 부분이 있지만 알아두어야 할 몇 가지 중요한 차이점도 있습니다 

앞서 살펴본 세 가지 구성 요소인 test function, expectation, suite을 비교해 보겠습니다

 

test function

XCTest의 테스트는 이름이 ‘test’로 시작하는 모든 메서드입니다 

하지만 Swift Testing은 @Test 속성으로 명시적으로 표시하므로 모호함이 없습니다

Swift Testing은 더 많은 종류의 함수를 지원하므로 원하는 경우 유형에서 인스턴스 메서드를 사용할 수 있으며 정적 또는 전역 함수도 사용할 수 있습니다 

XCTest와 달리 Swift Testing은 테스트별 또는 모음별로 정보를 지정할 수 있는 특성을 지원합니다

Swift Testing은 병렬화에 대한 접근 방식도 다릅니다

Swift 동시성을 사용해 프로세스 중에 실행되며 iPhone 및 Apple Watch와 같은 물리적 기기를 지원합니다

 

expectation

이 두 시스템 사이의 기대치는 매우 다릅니다 

XCTest는 이 개념을 assert라고 하며 이를 나타내기 위해 XCTAssert로 시작하는 많은 함수를 사용합니다

Swift Testing은 다른 접근 방법 두 가지를 사용합니다 두 개의 기본 매크로 #expect#require입니다

많은 특수 함수가 필요하지 않고 그 대신 일반 표현식과 언어 연산자를 #expect 또는 #require에 전달할 수 있습니다

예를 들어 이중 등호를 사용하여 같음을 확인하거나 > 연산자를 사용하여 두 값을 비교할 수 있습니다

또한 반대 연산자를 사용하여 expect를 무효화할 수도 있습니다

 

 

테스트 실패 발생 후 테스트 중단을 처리하는 방법도 다릅니다

XCTest에서는 continueAfterFailure 속성을 false로 지정한 다음 이후에 실패하는 assert가 있으면 테스트가 중단됩니다

Swift Testing에서는 모든 기대치를 필수로 만들 수 있습니다 #require를 #expect 대신 사용하면 실패 시 오류가 발생합니다 

이를 통해 테스트를 중단할 expect를 선택할 수 있으며 테스트 진행에 따라 대체할 수도 있습니다

 

Suites

 

XCTest는 클래스만 지원하며 클래스는 XCTestCase에서 상속해야 합니다

Swift Testing에서는 구조체, 액터 또는 클래스를 사용할 수 있는데 구조체 사용을 추천합니다 값의 의미론을 사용하여 의도하지 않은 상태 공유로 인한 버그를 피할 수 있기 때문입니다

모음은 @Suite 속성을 사용해 명시적으로 표시할 수 있습니다 하지만 테스트 함수 또는 중첩된 모음을 포함하는 모든 유형에는 암시적입니다 표시 이름이나 다른 특성을 지정할 때만 필요합니다

각 테스트 실행 전 로직 수행을 위해 XCTest는 여러 setUp 메서드를 제공하지만 Swift Testing은 이를 위해 유형의 이니셜라이저를 사용합니다 비동기 또는 예외가 될 수 있습니다

각 테스트 후에 로직을 수행해야 하는 경우 디이니셜라이저를 포함할 수 있습니다 디이니셜라이저는 모음 유형이 액터 또는 클래스인 경우에만 사용할 수 있으며 모음에 구조체 대신 참고 유형을 사용하는 가장 일반적인 이유입니다

마지막으로 Swift Testing에서는 중첩된 유형을 통해 테스트를 하위 그룹으로 그룹화할 수 있습니다

 


XCTest와 Swift Testing 테스트는 단일 대상에 공존할 수 있으므로 마이그레이션하기로 했다면 점진적으로 진행할 수 있으며 먼저 새 대상을 만들 필요가 없습니다

 

테스트 메서드가 하나만 있는 XCTest 클래스의 경우 전역 @Test 함수로 마이그레이션하는 것을 고려하세요 그리고 테스트 이름을 정할 때 더 이상 ‘test’를 앞에 붙일 필요가 없습니다

 

Swift Testing이 지원되지 않는경우는 아래와같습니다

XCTest를 계속 사용해야 하는 테스트가 있는데 XCUIApplication과 같은 UI 자동화 API나 XCTMetric과 같은 성능 테스트 API를 사용하는 테스트는 Swift Testing에서 지원되지 않습니다

또한 Objective-C로만 작성할 수 있는 모든 테스트에도 XCTest를 사용해야 합니다

하지만 다른 언어로 작성된 코드의 유효성을 검사하는 테스트를 Swift로 작성하는 데는 Swift Testing을 사용할 수 있습니다

마지막으로 XCTest 어설션 함수를 Swift Testing에서 호출하거나 또는 그 반대로 #expect 매크로를 XCTest에서 호출하지 마세요

 

 

Open source

Swift 동시성을 지원하는 모든 Apple 운영 체제뿐만 아니라 Linux와 Windows에서도 작동합니다 

그리고 중요한 것은 이 모든 플랫폼에서 공통 코드베이스를 사용한다는 것입니다! 

이는 여러 구현이 있었던 XCTest에 비해 크게 개선된 점입니다 

이는 플랫폼 사이를 이동할 때 테스트가 훨씬 더 일관적으로 동작하고 플랫폼 간 기능적 동등성이 향상된다는 의미입니다

 

터미널에서 swift test를 입력하여 이 패키지를 테스트할 수 있습니다

그러면 XCTest 테스트와 Swift Testing이 모두 실행됩니다 

콘솔은 다채로운 출력으로 pass 및 fail 결과를 표시하고 Xcode에 표시되는 것과 유사한 자세한 실패 메시지를 포함합니다

반응형