본문 바로가기
iOS/iOS

XCTest tips and tricks that can level up your Swift testing

by 헤콩 2021. 3. 3.
반응형

iOS에서의 Unit Test를 위해 XCTest에 대해 알아보던 중 XCtest tips and tricks that can level up your Swift testing 이라는 블로그를 보게 되었습니다. XCTest에 대한 내용과 참고 게시물의 내용을 함께 번역 및 정리해보면 좋을 것 같았습니다. 중간중간 제가 이해가지 않는 tip들은 직접 예시코드를 작성해보면서 이해해보려고 노력해봤는데, 아직까지는 좀 더 학습이 필요한 것 같아요 :) 그럼 먼저 XCTest가 무엇인지부터 알아볼까요?

 

 

What is XCTest?

먼저 애플에서는 XCTest에 대해 이렇게 말하고 있습니다.

 

Create and run unit tests, performance tests, and UI tests for your Xcode project.

 

네. 말 그대로 XCTest는 Xcode 프로젝트를 유닛 테스트 하기 위해 사용하는 프레임워크입니다. 혹시 유닛 테스트가 무엇인지, TDD가 무엇인지 모르시는 분들은 Unit Test와 TDD에 대해서 먼저 알아보고 와주세요 :)

 

 

XCTest는 많은 이점을 가지고 있지만, 그 중 일부는 문서자료가 부족하거나 이미 많이 사용되고 있는 것들에게 가려져서 사람들이 잘 모르는 경우가 있습니다. 그래서 오늘은 여러가지 tip을 소개해보려합니다.

 

 

1. Use XCTUnwrap instead of force wrapping optionals

옵셔널 값을 강제로 해제시키게 되면, 테스트 코드에서 충돌이 발생할 수 있습니다. 그럴 경우 아무런 출력이나 리포트가 발생하지 않을 수 있습니다. 강제 언래핑(!) 대신에 XCTUnwrap을 사용하면 nil 값을 언래핑할 때 실패 메시지를 출력하고 테스트를 fail시킵니다. nil 값에 대해 좀 더 유연하게 테스트할 수 있게 되죠.

 

 

2. Set continueAfterFailure to false

테스트 코드를 작성할 때 여러개의 assertion 을 작성하는 경우가 있습니다. 예를 들면, 첫 번째 assert가 배열의 사이즈를 검사하고, 두 번째 assert가 배열의 마지막 요소를 검사할 수도 있겠죠. 이 경우 첫 번째 assert가 실패하게 되면 이후에 나오는 assert 결과들은 사실 볼 필요가 없어집니다. 귀찮을 뿐이죠.

이 경우에 continueAfterFailure를 false로 설정해준다면 이런 귀찮음을 간단하게 해결할 수 있습니다.

 

 

3. Use KVO observing to test asynchronous expectations

keyValueObservingExpectation을 사용하면, mock 객체가 NSObject의 하위객체인 경우 우리는 메서드 호출을 기다리게 할 수 있습니다. 사실 저는 이 말이 뭔지 잘 몰라서 제가 직접 예시 코드를 작성해봤어요.

 

먼저 mock 객체를 하나 만들어주었습니다.

 

그리고 이 객체의 name을 테스트 하도록 해주었죠.

처음에는 "Jamie"라는 name을 가진 객체로 만들어주었다가 중간에 "Bob"으로 name을 바꿔주었습니다. 그리고 2초뒤에 name을 확인하도록 해주었습니다.

 

아직까지는 어떻게 활용해야할지 잘 모르겠지만 어떤 기능을 해주는지는 조금 이해를 했습니다.

 

 

4. Assert types with XCTAssert and is

이건 우리가 type만을 다룰 때 유용하게 사용할 수 있습니다.

 

앗 그리고 failure message는 막 엄청나게 도움이 된다고 생각하시면 안됩니다.. 보통 custom description을 추가하는 걸 권장하고 있어요.

 

 

5. Keep XCTAssertEqual parameter order consistent

XCTAssertEqual에 대한 애플 문서를 보면 예상 값이나 실제 값 중 어떤게 우선적으로 작성되어야 하는지 알려주지 않습니다. 그 중 하나를 골라서 어떤 테스트를 위한 것인지 분명하게 하는 것이 좋습니다. 개인적으로는 실제 값을 먼저 입력하여 모든 상수가 끝에 오도록 하는 것을 선호합니다.

 

예를 들어, 검사해야할 실제 값이 person.name이고 예상되는 값이 "Jamie"라면 아래와 같이 작성하는 걸 선호한다는 말이죠.

 

 

6. Extract helpers judiciously

"무언가를 수행" 하기 위해 3줄 이상의 코드를 작성해야한다면, 코드를 분리하여 helper 함수를 만드는 것이 좋습니다. helper 함수에는 테스트 설정, 작업, assertion 등이 작성될 수 있죠. XCTestCase는 클리스이기 때문에 우리가 원하는 함수를 추가할 수 있습니다. 대신 helper가 "test"라는 이름으로 시작하지 않도록 하는게 좋습니다. 만약 test로 시작한다면 테스트로 실행됩니다.

 

이게 무슨말이지? helper 함수는 또 뭐고? 라고 생각하실 수 있습니다.

쉽게 말하자면, 클린 코드를 읽어보신 분은 아시겠지만 최대한 함수를 분리하여 한 함수 안에 작성되는 코드의 양을 줄이는 게 좋다고 합니다. 그래서 함수를 아래처럼 분리해서 코드가 복잡해지지 않게 작성할 수 있다는 거죠. (물론 아래 예시는 2줄밖에 되지 않지만.. helper 함수가 뭘 뜻하는지 보여주고 싶어서 작성했습니다)

let persons: [Person] = ..

func testPersons throws {
    let count = countForFemale(persons)
    XCTAssertEqual(count, 5)
}

// helper
func countForFemale(_ list: [Person]) -> Int {
    return list.filter { $0.gender == "female" }.count
}

 

그리고 Xcode 11에서 #filepath 와 #line 으로 함수를 정의하는 경우에 테스트 실패를 호출자에게 전달하지만, Xcode 12는 이를 자동으로 수행합니다. 👉filepath와 line이 뭔지부터 알아볼 필요가 있습니다..

 

 

7. Add test extensions to reduce noise

우리가 테스트하고자 하는 객체에 extension을 사용해서 테스트할 때 좀 더 빠르고 유연하게 처리할 수 있습니다. 테스트할 때는 좀 더 간결하게 테스트하고자 하는 것에 집중할 수 있게 되죠.

 

 

8. Conform to Equatable to combine assertions

모델을 Equatable로 만들면 각 속성에 대한 assertion을 작성하지 않고도 전체적으로 테스트 할 수 있습니다. 더 많은 예제를 보고 싶다면 Better unit testing with Swift 게시물을 참고해보세요.

 

 

9. Always make your test throw

throws 키워드를 테스트 함수 끝에 추가하면, 발생하는 모든 exception을 잡아내고, 테스트하는 내내 ? 또는 ! 없이 try를 사용할 수 있습니다. 오류가 발생할 것으로 예상되지 않더라도 문제가 되지 않으니 throws 키워드를 추가하는게 좋겠죠? :)

 

 

10. Use protocols and mock extensively

▪️Mock 객체는 어떤 함수가 무엇으로 호출되는지를 protocol을 통해 구현합니다.

▪️Static Dependency는 let 변수로 선언되며, 초기화 시에 주입됩니다.

▪️Mock 객체는 Equatable로 선언되어야 하며, 테스트 시에 XCTAssertEqual를 사용할 수 있어야 합니다.

 

 

11. Use XCTAssertIdentical in Xcode 12.5

Xcode 12.5의 Beta release note에는 다음과 같은 내용이 있습니다.

 

"XCTest will include XCTAssertIdentical and XCTAssertNotIdentical APIs to assert

whether two object instances are identical (the same instance) and are stricter

than XCTAssertEqual by using the === operator instead of == in Swift."

 

즉, XCTAssertIdenticalXCTAssertNotIdentical은 Swift의 == 대신 === 연산자를 사용하여 두 개의 객체 인스턴스가 동일한 인스턴스인지, XCTAssertEqual보다 stricter한지 여부를 확인합니다.

 

 

 

 

 

반응형

'iOS > iOS' 카테고리의 다른 글

키체인 (Key Chain)  (0) 2021.04.13
GCD (Grand Central Dispatch)  (0) 2021.03.23
Frame과 Bounds  (0) 2021.02.26
Data Binding in MVVM on iOS  (2) 2021.01.07
ARC (Automatic Reference Counting)  (0) 2020.12.22

댓글