반갑습니다 집주인들 기술팀장 박효준입니다~
이번에 테스트 코드를 작성하면서 testable이 무엇인가!에 대해 궁금해져서 정리해보았습니다!
레츠 기릿~~
문제 상황
MHDomain이라는 모듈 안에 MHDomain 동적 라이브러리와 MHDomainTests 정적 라이브러리가 있고,
MHPresentation 모듈 안에 MHPresentation 동적 라이브러리와 MHPresentationTests 정적 라이브러리가 있다.
테스트를 진행할 때 import MHDomain 을 해주어야 하는데,
이 때 import 앞에 @testable 키워드를 사용할 수 있다.
무슨 역할을 하는 지 알아보자
문제 해결
@testable 키워드
테스트 대상 모듈의 접근 수준에 영향을 주는지 여부이다.
- 테스트 접근성 증가:
- Swift의 기본 접근 수준은 internal인데, 즉 동일 모듈 내에서만 접근할 수 있는 게 기본이다.
- @testable import를 사용하면 테스트 모듈에서 internal 멤버를 접근할 수 있게 되어, 단위 테스트 작성이 훨씬 용이해진다.
- 테스트 커버리지 향상:
- 일반 import로는 테스트할 수 없는 모듈 내부의 구현 세부 사항(internal 멤버)도 테스트할 수 있다.
- 단위 테스트에서 비즈니스 로직이나 헬퍼 메서드 등을 보다 직접적으로 검증할 수 있다.


실제로 비즈니스 로직을 테스트 해야하는데 어떻게 해야할 지 몰라서 여쭤봤었고,
그래서 private 혹은 internal 메소드를 다른 타겟에서 사용한다는 관점을 빌려 public으로 바꾸려 했었다가 팀원의 조언으로 멈췄다.
실제 적용사례
MHPresentation 모듈 안에 MHPresentation 동적 라이브러리와 MHPresentationTests 정적 라이브러리가 있다.
MHDomain 테스트할 때는 외부 모듈의 접근이 잦으니 public으로 되어있는 경우가 많은데,
MHPresentation는 대부분 internal로 구현이 되어 있다.
public final class HomeViewModel: ViewModelType {
func transform(input: AnyPublisher<Input, Never>) -> AnyPublisher<Output, Never> {
input.sink { [weak self] event in
switch event {
case .viewDidLoad:
self?.fetchMemorialHouse()
case .selectedCategory(let index):
self?.filterBooks(with: index)
}
}.store(in: &cancellables)
return output.eraseToAnyPublisher()
}
위 코드의 경우 그러면 저 transform을 public으로 해줄 거냐 ?
protocol ViewModelType {
associatedtype Input
associatedtype Output
@MainActor
func transform(input: AnyPublisher<Input, Never>) -> AnyPublisher<Output, Never>
}
그러면 위 프로토콜 자체도 public으로 바꿔줘야 한다.
이게맞나..?
라는 생각이 들 때 위 개념이 떠올라서 @testable을 붙이니까 MHPresentation 내부에 있는 internal 메소드도 사용이 가능했다 ^◡^
배운 점
- @testable 키워드의 기능을 알게 되었다.
- 접근 제어자를 고려하며 테스팅을 해볼 수 있었다.
참조 링크
'Swift' 카테고리의 다른 글
Test가 Serial하지 않군요! (0) | 2024.12.01 |
---|---|
Swift의 전역 변수 초기화 시점 (1) | 2024.11.30 |
XCTest vs Swift Testing (0) | 2024.11.30 |
디버깅할 때, print가 아닌 OSLog 사용을 위한 Logger 구현 (0) | 2024.11.23 |
카메라 및 사진 권한 에러 (1) | 2024.11.21 |