Skip to content

[기술 공유] Unit Test 할 때 @testable의 역할이 뭘까 ?

00me edited this page Nov 28, 2024 · 1 revision

문제 상황

MHDomain이라는 모듈 안에 MHDomain 동적 라이브러리MHDomainTests 정적 라이브러리가 있고,

MHPresentation 모듈 안에 MHPresentation 동적 라이브러리MHPresentationTests 정적 라이브러리가 있다.

테스트를 진행할 때 import MHDomain 을 해주어야 하는데,

이 때 import 앞에 @testable 키워드를 사용할 수 있다.

무슨 역할을 하는 지 알아보자


문제 해결

@testable 키워드

테스트 대상 모듈의 접근 수준에 영향을 주는지 여부이다.

  1. 테스트 접근성 증가:
  • Swift의 기본 접근 수준은 internal인데, 즉 동일 모듈 내에서만 접근할 수 있는 게 기본이다.
  • @testable import를 사용하면 테스트 모듈에서 internal 멤버를 접근할 수 있게 되어, 단위 테스트 작성이 훨씬 용이해진다.
  1. 테스트 커버리지 향상:
  • 일반 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 키워드의 기능을 알게 되었다.
  • 접근 제어자를 고려하며 테스팅을 해볼 수 있었다.

참조 링크

https://zeddios.tistory.com/1078

Clone this wiki locally