iOS/iOS

iOS) 유닛테스트(Unit Test) 테스트 코드를 작성해보자 (2/2)

소들이 2025. 6. 30. 19:14

 

 

 

안녕하세요! 소들입니다 :)

저 퇴사 했습니다 🎉~~~~~ (뜬금)

백수입니다만? 이제 월급이 안 들어옵니다만?

나스닥아,,!! 힘내,,!!

 

6월 1달 간은 저에게 휴식을 주는 시간을 가졌구요

7, 8월은 제 스스로 느꼈던 기술 부채와 한계를

 조금 메우는 시간을 가져보려고 해요 :D

 

백수가 되었지만 .. 블로그 글은 잘 못쓸 것 같네요ㅠㅠ

글 쓰는 게 사실 너무 오래 걸려서,,

이 글을 마지막으로 언제 올진 모르겠습니다

3분기 이후에 재취업하면 아마?도?

 

쨌든 마무리는 짓자는 마인드로

사실 이미 오래 전에 끝냈어야 할,,

유닛 테스트와의 마지막 시간을 보내보려고 합니다

그 테스트,, 좋은 테스트였ㅇㅓ,,

 

 

 

 

1. Testing System이 뭔가요?

 

여러분 프로젝트를 만들 때 말이죠

 

 

 

이렇게 이렇게 Testing System 영역에 총 세가지가 있잖음?

말그대로 테스트 시스템을 고르는 건데,

 

None은 말 그대로 Test 파일 자체가 생성이 안 됨!

 

근데 나머지

 

XCTest for Unit and UI tests

Swift Testing with XCTest UI Tests

 

이 두개 같은 경우 암거나 체크하고 프로젝트를 만들면

 

 

 

 

이렇게 Tests 파일UITests란 파일이 생김

(저번 포스팅에서 테스트는 단순 유닛도 있지만 최종적으로 UI 테스트까지 간다 했져?) 

 

그럼 두 개의 차이가 뭐냐하고 묻는다면,

Swift Testing이 좀 더 신생아임

XCTest for Unit and UI tests가 기존에 사용하던 방법으로

클래스와 메서드 기반으로 작성한다면,

Swift Testing with XCTest는 XCode 15부터 도입

함수와 매크로 기반으로 작성을 함

(불현듯 떠오르는,,, 매크로 부수고 부서지기,,)

 

이번 포스팅에선 그럼 둘 중에 뭐를 공부하겠냐 한다면

좀 더 오래 전부터 사용해오던 

XCTest for Unit and UI tests

에 대해서 공부 할 것임다

 

 

 

 

2. 테스트 코드 살펴보기

 

XCTest for Unit and UI tests를 체크하고

프로젝트를 만들면

 

 

 

네 대충 이런 코드가 나오는데요

먼저 첫 줄에 import 된 것들을 먼저 보겠음

 

import XCTest
@testable import SodeulTest

 

 

XCTest는 테스트에 필요한 메서드들을 사용하기 위해 import 해야함

근데 밑에 @testable이 붙고 내 프로젝트가 임포트 되어 있잖음?

 

사실 

XCTest for Unit and UI tests

로 만들 경우, 테스트가 테스트 전용 모듈로 분리가 되어버림

따라서 내 프로젝트를 무조건 import를 해야만 접근이 가능함!

(테스트 자체가 내 프로젝트의 기능을 테스트 하는 것이기 때문)

 

근데 모듈 간에 접근이 가능하려면 

Public 접근 제어자부터 가능하잖음??

근데 그렇다고 내가 테스트를 위해 모든 접근 제어자를 public으로 바꿀 수도 없는 노릇이거,,

 

때문에 이것 때문에 붙은 것이 @testable임!

@testable을 붙여줄 경우,

테스트를 할 때만 private이 아닌 접근 제어자는 모두 접근 할 수 있음

 

실제로 보면

 

 

 

 

이렇게 내 프로젝트에 SodeulViewController를 만들고

접근제어자를 public으로 해주지 않았지만

 

 

 

 

이렇게 별도의 모듈인 테스트에서 SodeulViewController에 접근할 수 있음!

이것이 가능한 게 바로 @testable이란 것 때문임

만약 프로젝트 import 시에 @testable이란 것을 삭제할 경우,

 

 

 

 

이렇게 SodeulViewController를 찾을 수 없다고 뜸!

이해 가셨져?(물론 SodeulViewController를 public으로 하면 접근 가넝)

근데 그렇게 테스트를 위해 은닉성 개나 주지 말구

얌전히 @testable을 사용합시당 ㅎㅁㅎ

 

 

 

 

3. XCTestCase는 뭘까?

 

 

 

 

그 다음으로 살펴볼 것은 XCTestCase라는 것인데

Unit Test / UI Test를 작성할 경우, 반드시 이 XCTestCase라는 것을 상속 받아야 함!

상속,,? 저아이,, 클래스인가요,,?

 

 

@interface XCTestCase: XCTest

 

 

옵젝씨 어서오고

넵 클래스임 그리고 얘는 또 XCTest를 상속받는데,

얘는 documentation을 보면 말이져

 

 

 

 

이런 메서드들이 있는데,

테스트 코드를 체크하고 프로젝트를 생성하면,

자동으로 들어 있던

 

 

 

 

setupWithError, tearDownWithError 등이 이 XCTest의 멤버인 것을 볼 수 있음

 

다시 위에 올라가서 이름이 살짝씩만 다른

setup, tearDown 메서드들을 살펴보면

 

에러와 함께 사용할 경우 (WithError)

비동기로 구현해야 할 경우(compoletion 또는 async)

그외의 경우

 

이렇게 세 가지 버전으로 있는 것

때에 따라서 알아서 쓰면 됨

만약 동시에 여러 개의 setup / tearDown 메서드를 쓸 경우,

불리는 순서가 다르니 이것만 유의 하셈

 

 

그래서 도당체 setup이랑 tearDown이 뭔데?

 

먼저 setup테스트가 실행되기 전에 자동으로 불림

따라서 테스트에 필요한 초기화 셋업하는 코드를 구현

 

tearDown테스트가 실행된 후 자동으로 불림

따라서 테스트에서 사용한 리소스가 있다면 정리하는 코드를 구현

 

자, 여기서 주의해야 할 점이

먼가 요거,, 우리가 기존에 알던 init,,, deinit 같은 느낌이거덩여?,, 할 수 있는데

TDD의 법칙 중 "테스트는 독립적이어야 하며, 다른 테스트에 영향을 주면 안됨"

이라는 법칙 기억 남?

 

따라서 이 SodeulTestTests 클래스가 생성될때 setup이 불리고,

사라질 때 tearDown이 불리는 게 아님!

테스트 한개 당 한번씩 setup, tearDown이 불리게 되어 있음

 

보여주자면,

 

 

 

 

이렇게 testA, testB, testC가 만약 있다 할 때

(테스트 만드는 법은 바로 뒤에 설명할 거)

SodeulTestTests 전체 테스트를 실행시키면

 

 

 

참고로 맨 왼쪽의 요 ▶️ 버튼을 누르면

SodeulTestTests에 작성된 전체 테스트가 진행 됨

 

쨌든 그러면 말이져

 

 

 

 

이렇게 테스트가 실행된 만큼 setup, tearDown이 불리는 것을 알 수 있음!

 

 

+ 아 그리구 

 



func testPerformanceExample() throws

 

 

이 메서드도 자동으로 생성되어 있는데, 

얘도 테스트 코드인데, 코드가 얼마나 빠르게 실행되는지를 테스트 해주는 것임!

 

 

 

 

4. 테스트 코드 작성 방법

 

테스트 코드라고 막... 이름 막.. 지으시면 안 되구여,,

나름대로 규약이 있음

 

먼저 필수 조건이라 함은

 

 

func testA() { }

 

 

이런 식으로 테스트 함수는 무조건 test로 시작해야함!

이건 필수 조건임

아니면 테스트로 인식 안함

그리고 보통 네이밍을 지을 때는

 

 

test대상_작업_예상되는결과

 

 

이런 패턴으로 작성함!

따라서 

 



func testAddOperator_WhenAdded_returnsCorrectSum()

 

 

 

이런 식으로 테스트 이름만으로도 

어떤걸 테스트하고, 어떤 결과를 예상하는지를 알 수 있게 작성해야 함!

만약 난 영어 싫어! 척화비 좋아! 하면

 



func test더하기연산자_더하기_올바른합리턴()

 

 

이렇게 한글로 작성해도 됨!

다만 앞에 test는 무조건 붙여주기로!!

 

이제 테스트 코드 함수의 이름을 작성 했으면,

테스트 코드 내부를 작성해야 하잖음??

 

이때 쓰는 패턴이 gwt 패턴이라고 해서,

given - when - then

 세 가지 패턴으로 테스트 코드를 작성하는 것을 말함

 

given준비 단계,

when실행 단계,

then검증 단계

 

이렇게 세가지로 진행되는데,

막 세 단계를 무조건 지켜야돼!!! 이런 건 아님

필요에 따라 준비 단계가 생략되거나,

특정 단계를 반복해도 됨!

 

+

비슷하게 tripleA 패턴으로

arrange 준비단계,

act 실행 단계,

assert 검증 단계

로도 쓰긴 함! 패턴은 알아서 쓰셈

 

 

따라서 gwt 패턴 나오니까 갑자기

디자인 패턴 생각나면서 이게 모야 싶으시져?

진짜 별거 없음ㅎ

 

 

func testAddOperator_WhenAdded_returnsCorrectSum() {
    // Given (준비 단계)
    let a = 1
    let b = 2
    
    // When (실행 단계
    let result = a + b
    
    // Then (검증 단계)
    XCTAssertEqual(result, 3)
}
 

 

 

그냥 요렇게 주석으로 구분 해주시면 됨니다

진짜 별거 없져?

근데 test에서 검증 단계에 쓰이는 건

막,, 머 if result == 3 이런거 아니구영,,;;

XCTAssertEqual 같이 XCTest에 내장된 Assert 메서드를 사용함니당

 

따라서

 

 

 

이렇게 보면 값이 nil인지, true인지, false인지 등등

검증할 수 있는 메서드가 많이 존재하니 여기서 골라 쓰심 됨

 

 

 

 

5. 테스트 코드를 작성했으니 테스트를 돌려보자!

 

 

 

 

 테스트 클래스 자체에 있는 Test Diamond를 눌러서 테스트를 진행 시킬 수도 있고,

이 경우 클래스 안에 작성된 모든 테스트가 실행됨

(참고로 테스트가 성공되면 초록색으로 변함,

테스트가 진행되지 않았을 경우, 회색 ▶️ 이 마크임)

 

 

 

 

혹은 이렇게 테스트 메서드마다 붙어있는 Test Diamond로

개별 테스트를 진행할 수도 있음

 

 

func testAddOperator_WhenAdded_returnsCorrectSum() {
    // Given (준비 단계)
    let a = 1
    let b = 2
    
    // When (실행 단계
    let result = a + b
    
    // Then (검증 단계)
    XCTAssertEqual(result, 100000)
}
 

 

 

만약 이렇게 검증 단계에서 잘못된 결론이 도출된 경우,

 

 

 

 

 

이렇게 테스트가 실패했다는 다이아몬드로 바뀌면서,

빨간 오류가 뜸!

 

 

 

 

6. 코드가 얼마나 테스트 되었나?

 

 

 

이렇게 리포트 네비게이터를 누르면 Tests 항목을 볼 수 있는데,

여기는 내가 테스트를 실행할 때마다 결과를 리포트로 제공해줌!

따라서 전체 테스트의 결과를 확인할 수 있음

 

코드 커버리지가 44%로 되어 있는 건,

전체 코드에서 얼마큼 테스트를 진행했는지를 나타내는 것인데

(전체 코드에서 테스트를 44프로 했단 거겠져?)

100%로 만드는것은 사실 불가능이고 ㅋㅅㅋ

보통 60~80%를 목표로 한다고 함

근데 수치보다 제대로 기능 테스트 했는가가 더 중요함

 

그리거 마지막으로

 

 

 

 

이렇게 Editor에서 Code Coverage를 켜면

 

 

 

 

이렇게 옆에 바가 생기면서 숫자가 있음!

 이 숫자는 이 테스트를 몇 번 진행했는지를 나타내는 것이고,

테스트를 한 곳은 이렇게 초록색으로 표시해줌!
(테스트를 하지 않은 경우 숫자는 0으로 뜨고 빨간색으로 표시 해줌)

 

 

 

 

 

 

 

 

.

.

.

후.. 오랜만에 글쓰니까 힘드네용,,

그래도 Unit Test에 대해 공부할 시간이 없으셨던 분들은 참고가 되셨길 바라고!!!

그럼 이만~~~~~~~ 나 화이팅 😉

Swift Testing에 대해선 나중에~~~ 다루겠슴니다