서론
최근 테스트와 관련된 포스팅을 작성했습니다.
그리고 작업을 진행하면서 꾸준히 테스트 코드를 작성하고 있어요.
Swift Testing의 좋았던 점과 테스트 코드를 작성하면서 느낀 점을 공유해보고 싶어서 또 포스팅을 쓰러 왔어요 ㅎㅎ
테스트 코드를 왜 작성하기로 결심했는지 등은 이전 포스팅을 참고해 주세요.
Swift Testing
제가 담당하는 앱은 글로벌 서비스라 최근에 최소지원버전을 iOS 15로 올렸을 만큼 최신화된 프로젝트는 아니에요.
Swift Testing 처음 발표를 들었을 때는 "이번에도 나는 못 쓰지 않을까?" 생각이 있었어요.
다행히 Swift Testing은 그런 걱정과 달리 편하게 사용할 수 있었고, 충돌이 발생하는 등의 문제가 발생한 적은 한 번도 없었습니다. 그덕에 신규 테스트코드는 모두 Swift Testing으로 작성하고 있어요.
Swift Testing에서 가장 좋았던 점은
- setUp/tearDown을 안 써도 된다는 것
- argument를 이용해 동일한 성격의 시나리오를 묶을 수 있다는 것
- 결과 비교 메서드 종류가 적다는 것
이었습니다.
setUp/tearDown
setUp/tearDown은 테스트 메서드 수행 전후에 객체를 설정, 초기화하는 역할입니다.
기존에는 Repository라던가 Storage처럼 DI에 사용되는 객체를 공유해서 이를 초기화하는 작업이 필요했어요.
하지만 Swift Testing은 테스트 함수가 실행될 때 독립적인 구조체 인스턴스가 생성되어 개발자가 따로 초기화하지 않아도 괜찮아요.
덕분에 테스트 코드에만 집중할 수 있었습니다.
argument로 시나리오 묶기
Swift Testing에는 argument를 통해 테스트 코드를 실행할 때 사용되는 모델을 주입할 수 있어요.
기존에는 같은 로직이라도 input/output이 다르다면 테스트 함수를 분리해야 했어요.
혹은 테스트 함수 내부에서 반복문을 돌려 직관적이지 않게 작성해야 했습니다.
Swift Testing을 사용하면서 argument로 테스트 소스를 주입할 수 있게 되면서 테스트 코드가 심플해졌습니다.
예를 들어 "로컬에 A가 있을 때 Input A가 들어오면 output은 target A와 같다"를 작성하면,
@Test(
"테스트 시나리오",
arguments: FetchTestCase.allCases
)
func fetch(testCase: FetchTestCase.Model) async {
let repository = Repository(storage: storage, service: service)
let useCase = UseCase(repository: repository)
await useCase.save(data: testCase.localData)
let output = useCase.fetchData(of: testCase.input)
#expect(output == testCase.target)
}
위처럼 input에 대한 output을 주입받아서 같은 시나리오의 여러 케이스를 하나로 묶을 수 있었습니다.
테스트 코드 안에는 테스트에 필요한 코드만 존재하기 때문에 코드 파악의 난이도가 쉬워졌어요.
그리고 "테스트 시나리오"라고 적은 곳에는 한글로 편하게 작성할 수 있는데요.
기존에는 테스트 함수 이름을 한글로 쓰면 띄어쓰기가 안 되서 불편했는데 여기는 그런 제약이 없어서 너무 좋았습니다.
(Xcode에도 저 부분에 작성한 내용이 표시되어서 좋습니다 ㅋㅋ)
enum FetchTestCase {
typealias Model = (
localData: DataType,
input: InputType,
target: TargetType
)
static let allCases: [Model] = [
( // 테스트 시나리오 1
.localA,
inputA,
.targetA
),
( // 테스트 시나리오 2
.localB,
inputB,
.targetB
)
]
}
테스트 모델은 따로 정의해서 추후 다른 케이스가 추가되면 여기에 추가만 하면 돼요.
테스트 코드를 수정하지 않기 때문에 실수를 하게 될 가능성도 낮아졌습니다.
argument는 여러모로 활용처가 많았습니다.
지금 저는 단순히 데이터 1개를 target으로 뒀지만,
로직이 조금씩 다르다면 클로저를 전달하는 방향으로 활용해도 괜찮을 거 같네요!
(로직이 다르면 테스트 함수를 분리하는게 맞지 않나 생각도 들고요 ㅎㅎ;)
결과 비교 메서드 종류가 적다는 것
XCTest에서는 XCAssertEqual 어쩌구저쩌구 종류가 모두 분리되어 있습니다.
Swift Testing에는 #expect 하나로 웬만한건 다 커버가 가능해요.
Swift 문법으로 자연스럽게 조건을 작성할 수 있어서 가독성도 좋았습니다.
Swift에 자연스러운 문법으로 결과 비교를 한다는 점에서 Swift Testing 이름에 딱인 거 같아요 ㅋㅋ
테스트 코드를 작성하며 느낀 점
테스트 코드의 필요성을 느끼고 꾸준히 작성하고 있습니다.
테스트 코드를 먼저 작성하고 이를 기반으로 기능 구현을 하는 TDD까지는 아니더라도,
기능을 구현한 뒤 테스트 시나리오를 고민해서 테스트 코드를 작성하고 있어요.
TDD?
아무래도 테스트 코드를 먼저 작성하면 여러 시나리오를 떠올리게 해서 보다 다양한 시각으로 기능을 바라볼 수 있어요.
그게 TDD의 장점 중 하나라고 생각합니다.
그렇지만 이미 저는 기능 구현을 하기 전에 다각도로 고민을 하고,
대부분의 개발자도 그럴거라도 생각해서 굳이... 라는 생각이 들었네요.
하지만 "어떻게 기능 구현 전에 여러 방면으로 고민할 수 있을까?"를 고민하고 계신다면 좋은 선택지가 될거라고 생각합니다.
도움 받은 경험
테스트 코드를 작성해서 도움이 된 적이 있나요? 테스트 코드가 정말 잘못된 구현을 잡아주나요? 라는 의문을 가진 분들도 계실거에요.
내가 짠 코드를 수정했는데 왜 파악을 못할까? 그냥 실력이 없는거 아니야? 라는 생각도 있으실거고요.
근데 회사에 입사하니 다른 사람의 코드를 볼 때가 많고, 그 코드가 안정적으로 구현되지 않은 경우도 꽤 있더라고요. (제 경력이 길지 않음에도...) 심지어 실수를 확인하기 어려운 기능 코드일 때도 있어요. 이럴 때는 테스트 코드의 존재가 큰 보험으로 다가왔어요. 심리적 안정감이 차원이 다릅니다 ㅋㅋ
실제로 최근에 Objective-C 코드를 작업하다 실수를 하나 했는데요. 이와 관련된 테스트 코드에서 Failed가 나면서 버그를 잡은 경험이 있네요 ㅎㅎ 아... 내 선택이 틀리지 않았구나... 느꼈습니다.
어려운 점은?
기능 개발하기도 시간이 없는데 테스트 코드를 어떻게 짜요? 라고 생각을 할 수도 있고 팩트긴 합니다.
근데 생각해보면 QA 기간에 고생하는 거보다는 개발 기간에 고생하는게 낫더라고요.
다다음주면 테스트 코드를 작성한 이후 첫 QA인데요.
어떻게 될지 후기를 남겨보겠습니다 ㅎㅎ;
일단 지금은 실보다는 득이 더 크다고 느끼고 있어요.
추가로 저희는 Bitrise에서 테스트를 돌리고 있는데요.
Swift Testing이 테스트 리스트에는 표시가 안 되서 아예 안 잡히는줄 알았습니다.
근데 테스트에 실패하면 리스트에 뜨더라고요.
표시만 안 될 뿐 잘 캐치는 하는 거 같습니다 ㅎ;
마무리
아무튼 결론은 Swift Testing은 좋았다!입니다.
Swift Testing을 고민하시고 계시면 한 번 도입해 보세요!
아직은 초보 개발자입니다.
더 효율적인 코드 훈수 환영합니다!
공감과 댓글 부탁드립니다.
서론
최근 테스트와 관련된 포스팅을 작성했습니다.
그리고 작업을 진행하면서 꾸준히 테스트 코드를 작성하고 있어요.
Swift Testing의 좋았던 점과 테스트 코드를 작성하면서 느낀 점을 공유해보고 싶어서 또 포스팅을 쓰러 왔어요 ㅎㅎ
테스트 코드를 왜 작성하기로 결심했는지 등은 이전 포스팅을 참고해 주세요.
Swift Testing
제가 담당하는 앱은 글로벌 서비스라 최근에 최소지원버전을 iOS 15로 올렸을 만큼 최신화된 프로젝트는 아니에요.
Swift Testing 처음 발표를 들었을 때는 "이번에도 나는 못 쓰지 않을까?" 생각이 있었어요.
다행히 Swift Testing은 그런 걱정과 달리 편하게 사용할 수 있었고, 충돌이 발생하는 등의 문제가 발생한 적은 한 번도 없었습니다. 그덕에 신규 테스트코드는 모두 Swift Testing으로 작성하고 있어요.
Swift Testing에서 가장 좋았던 점은
- setUp/tearDown을 안 써도 된다는 것
- argument를 이용해 동일한 성격의 시나리오를 묶을 수 있다는 것
- 결과 비교 메서드 종류가 적다는 것
이었습니다.
setUp/tearDown
setUp/tearDown은 테스트 메서드 수행 전후에 객체를 설정, 초기화하는 역할입니다.
기존에는 Repository라던가 Storage처럼 DI에 사용되는 객체를 공유해서 이를 초기화하는 작업이 필요했어요.
하지만 Swift Testing은 테스트 함수가 실행될 때 독립적인 구조체 인스턴스가 생성되어 개발자가 따로 초기화하지 않아도 괜찮아요.
덕분에 테스트 코드에만 집중할 수 있었습니다.
argument로 시나리오 묶기
Swift Testing에는 argument를 통해 테스트 코드를 실행할 때 사용되는 모델을 주입할 수 있어요.
기존에는 같은 로직이라도 input/output이 다르다면 테스트 함수를 분리해야 했어요.
혹은 테스트 함수 내부에서 반복문을 돌려 직관적이지 않게 작성해야 했습니다.
Swift Testing을 사용하면서 argument로 테스트 소스를 주입할 수 있게 되면서 테스트 코드가 심플해졌습니다.
예를 들어 "로컬에 A가 있을 때 Input A가 들어오면 output은 target A와 같다"를 작성하면,
@Test(
"테스트 시나리오",
arguments: FetchTestCase.allCases
)
func fetch(testCase: FetchTestCase.Model) async {
let repository = Repository(storage: storage, service: service)
let useCase = UseCase(repository: repository)
await useCase.save(data: testCase.localData)
let output = useCase.fetchData(of: testCase.input)
#expect(output == testCase.target)
}
위처럼 input에 대한 output을 주입받아서 같은 시나리오의 여러 케이스를 하나로 묶을 수 있었습니다.
테스트 코드 안에는 테스트에 필요한 코드만 존재하기 때문에 코드 파악의 난이도가 쉬워졌어요.
그리고 "테스트 시나리오"라고 적은 곳에는 한글로 편하게 작성할 수 있는데요.
기존에는 테스트 함수 이름을 한글로 쓰면 띄어쓰기가 안 되서 불편했는데 여기는 그런 제약이 없어서 너무 좋았습니다.
(Xcode에도 저 부분에 작성한 내용이 표시되어서 좋습니다 ㅋㅋ)
enum FetchTestCase {
typealias Model = (
localData: DataType,
input: InputType,
target: TargetType
)
static let allCases: [Model] = [
( // 테스트 시나리오 1
.localA,
inputA,
.targetA
),
( // 테스트 시나리오 2
.localB,
inputB,
.targetB
)
]
}
테스트 모델은 따로 정의해서 추후 다른 케이스가 추가되면 여기에 추가만 하면 돼요.
테스트 코드를 수정하지 않기 때문에 실수를 하게 될 가능성도 낮아졌습니다.
argument는 여러모로 활용처가 많았습니다.
지금 저는 단순히 데이터 1개를 target으로 뒀지만,
로직이 조금씩 다르다면 클로저를 전달하는 방향으로 활용해도 괜찮을 거 같네요!
(로직이 다르면 테스트 함수를 분리하는게 맞지 않나 생각도 들고요 ㅎㅎ;)
결과 비교 메서드 종류가 적다는 것
XCTest에서는 XCAssertEqual 어쩌구저쩌구 종류가 모두 분리되어 있습니다.
Swift Testing에는 #expect 하나로 웬만한건 다 커버가 가능해요.
Swift 문법으로 자연스럽게 조건을 작성할 수 있어서 가독성도 좋았습니다.
Swift에 자연스러운 문법으로 결과 비교를 한다는 점에서 Swift Testing 이름에 딱인 거 같아요 ㅋㅋ
테스트 코드를 작성하며 느낀 점
테스트 코드의 필요성을 느끼고 꾸준히 작성하고 있습니다.
테스트 코드를 먼저 작성하고 이를 기반으로 기능 구현을 하는 TDD까지는 아니더라도,
기능을 구현한 뒤 테스트 시나리오를 고민해서 테스트 코드를 작성하고 있어요.
TDD?
아무래도 테스트 코드를 먼저 작성하면 여러 시나리오를 떠올리게 해서 보다 다양한 시각으로 기능을 바라볼 수 있어요.
그게 TDD의 장점 중 하나라고 생각합니다.
그렇지만 이미 저는 기능 구현을 하기 전에 다각도로 고민을 하고,
대부분의 개발자도 그럴거라도 생각해서 굳이... 라는 생각이 들었네요.
하지만 "어떻게 기능 구현 전에 여러 방면으로 고민할 수 있을까?"를 고민하고 계신다면 좋은 선택지가 될거라고 생각합니다.
도움 받은 경험
테스트 코드를 작성해서 도움이 된 적이 있나요? 테스트 코드가 정말 잘못된 구현을 잡아주나요? 라는 의문을 가진 분들도 계실거에요.
내가 짠 코드를 수정했는데 왜 파악을 못할까? 그냥 실력이 없는거 아니야? 라는 생각도 있으실거고요.
근데 회사에 입사하니 다른 사람의 코드를 볼 때가 많고, 그 코드가 안정적으로 구현되지 않은 경우도 꽤 있더라고요. (제 경력이 길지 않음에도...) 심지어 실수를 확인하기 어려운 기능 코드일 때도 있어요. 이럴 때는 테스트 코드의 존재가 큰 보험으로 다가왔어요. 심리적 안정감이 차원이 다릅니다 ㅋㅋ
실제로 최근에 Objective-C 코드를 작업하다 실수를 하나 했는데요. 이와 관련된 테스트 코드에서 Failed가 나면서 버그를 잡은 경험이 있네요 ㅎㅎ 아... 내 선택이 틀리지 않았구나... 느꼈습니다.
어려운 점은?
기능 개발하기도 시간이 없는데 테스트 코드를 어떻게 짜요? 라고 생각을 할 수도 있고 팩트긴 합니다.
근데 생각해보면 QA 기간에 고생하는 거보다는 개발 기간에 고생하는게 낫더라고요.
다다음주면 테스트 코드를 작성한 이후 첫 QA인데요.
어떻게 될지 후기를 남겨보겠습니다 ㅎㅎ;
일단 지금은 실보다는 득이 더 크다고 느끼고 있어요.
추가로 저희는 Bitrise에서 테스트를 돌리고 있는데요.
Swift Testing이 테스트 리스트에는 표시가 안 되서 아예 안 잡히는줄 알았습니다.
근데 테스트에 실패하면 리스트에 뜨더라고요.
표시만 안 될 뿐 잘 캐치는 하는 거 같습니다 ㅎ;
마무리
아무튼 결론은 Swift Testing은 좋았다!입니다.
Swift Testing을 고민하시고 계시면 한 번 도입해 보세요!
아직은 초보 개발자입니다.
더 효율적인 코드 훈수 환영합니다!
공감과 댓글 부탁드립니다.