Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TEST] Challenge Unit test code 작성 #200

Merged
merged 16 commits into from
Dec 3, 2023
Merged

[TEST] Challenge Unit test code 작성 #200

merged 16 commits into from
Dec 3, 2023

Conversation

kimscastle
Copy link
Contributor

[#198] TEST : Challenge Unit test code 작성

🌱 작업한 내용

  • Challenge관련 API의 Unit test code를 작성헀습니다
  • ChallengeViewModel의 로직을 검증하는 Unit test code를 작성했습니다
  • ChallengeViewController의 UI로직을 검증하는 Unit test code를 작성했습니다

🌱 PR Point

1.Challenge관련 API Unit test

  • 실제 비동기적으로 API를 서버에서 호출하게되면 시간이 오래걸리고 error가발생했을때에 대한 로직을 검증하기 어렵기때문에 urlSession의 역할을 대신해주는 urlSessionStub를 사용하여 기존에 사용하던 APIService객체의 로직을 검증했습니다
protocol LHURLSession {
    func data(for request: URLRequest) async throws -> (Data, URLResponse)
}

extension URLSession: LHURLSession {}

final class APIService: Requestable {
    
    private let session: LHURLSession
    
    init(session: LHURLSession = URLSession.shared) {
        self.session = session
    }
    
    func request<T: Decodable>(_ request: URLRequest) async throws -> T? {
        let (data, _) = try await session.data(for: request)
        ...생략...
        print("✨✨✨✨✨✨✨✨✨✨✨✨✨API호출성공✨✨✨✨✨✨✨✨✨✨✨✨✨")
        print(decodedData)
        return decodedData.data
    }
}
  • 1차리팩터링에서 의존성분리를 해놨기때문에 기존객체의 코드를 건드리지않고 프로토콜을 따르는 외부객체를 생성자에서 바꿔끼는 방식으로 urlSessionStub를 주입후 네트워킹의 로직과 결과를 테스트 했습니다

진행한 테스트

  1. User의 Challenge진척도 조회 API를 통한 ChallengeData를 잘 받아오는지를 테스트했습니다

2.ChallengeViewModel Unit test

  • 이미 API통신을 했을때 데이터가 잘 들어오는걸 API Unit test를 통해 검증했기때문에 ChallengeManagerStub를만들어 데이터가 우리가 원하는대로 들어왔을떄 combine의 input output에 따른 transform의 로직을 검증했습니다
  • 기존과 마찬가지로 DI를 통해 외부주입객체를 바꿔가면서 실제 ViewModel객체의 로직과 결과를 검증했습니다
final class ChallengeManagerStub: ChallengeManager {
    var returnValue: ChallengeDataResponse?
    
    func inquireChallengeInfo() async throws -> LionHeart_iOS.ChallengeData {
        guard let returnValue else { throw NetworkError.badCasting }
        return returnValue.toAppData()
    }
}

진행한 테스트

  1. viewWillAppear호출시 데이터가 AppData형태로 잘 변환되었는지를 테스트했습니다
  2. viewWillAppear호출하는 과정에서 error가 발생했을때 올바른 error를 발생하는지를 테스트했습니다
  3. bookmarkbutton이 눌렸을때 올바른 flowtype이 전달되는지를 테스트했습니다
  4. mypageButton이 눌렸을때 올바른 flowtype이 전달되는지를 테스트했습니다

3.ChallengeViewController Unit test

  • viewModel에서 stream을 통해 데이터가 잘 전달되는걸 viewModel의 unit test로 검증했기에 실제로 stream을 통해 받은 데이터가 viewcontroller내부의 UI component에 잘 반영되었는지를 테스트했습니다
  • viewcontroller또한 ViewModel을 의존성주입으로 받고있기에 ViewModelStub를 통해 ViewController의 UI관련 로직을 검증했습니다
final class ChallengeViewModelStub: ChallengeViewModel {
    
    enum FlowType { case bookmarkButtonTapped, myPageButtonTapped }
    
    var navigationSubject = PassthroughSubject<FlowType, Never>()
    private var cancelBag = Set<AnyCancellable>()
    let errorSubject = PassthroughSubject<NetworkError, Never>()
    var willPublishedData: ChallengeData?
    
    var inputData: ChallengeData!
    
    func transform(input: ChallengeViewModelInput) -> ChallengeViewModelOutput {
        
        input.navigationLeftButtonTapped
            .sink { [weak self] in
                self?.navigationSubject.send(.bookmarkButtonTapped)
            }
            .store(in: &cancelBag)
        
        input.navigationRightButtonTapped
            .sink {
                [weak self] in self?.navigationSubject.send(.myPageButtonTapped)
            }
            .store(in: &cancelBag)
        
        let viewWillAppearSubject = input.viewWillAppearSubject
            .flatMap { _ -> AnyPublisher<ChallengeData, Never> in
                self.willPublishedData = self.inputData
                return Just(self.inputData)
                    .eraseToAnyPublisher()
            }
            .eraseToAnyPublisher()
        
        return ChallengeViewModelOutput(viewWillAppearSubject: viewWillAppearSubject)
    }
}
  • ViewController의 unit test는 주로 실제 데이터가 UI에 잘 반영되었는지를 확인하였습니다, componet의 text와 실제 data의 값을 비교하고나 cell의 indexpath를 통해 cell내부의 label에 접근해서 text값이 cell에 들어가는 data와 동일한지, 특정cell에서 background color와 textcolor가 의도한대로 반영되었는지를 속성값을 직접 비교하며 테스트를 진행했습니다

진행한 테스트

  1. viewWillAppear의 호출시점이 ViewModel에 잘 전달되었는지를 테스트헀습니다
  2. API통신을 통해 얻은 data가 UserData를 보여주는 View에 잘 반영되었는지를 테스트했습니다
  3. API통신을 통해 얻은 data가 CollectionView의 각 Cell에 제대로 반영되었는지 테스트했습니다
  4. navigation버튼에 실제 action을 전달했을때 viewModel에 올바른 action type을 전달했는지를 테스트했습니다

📸 스크린샷

  • 목표로헀던 unit test를 진행한 객체의 coverage 70%이상을 달성했습니다
스크린샷 2023-12-01 오후 10 13 19

📮 관련 이슈

@kimscastle kimscastle added 🦁의성 의성's 🧪Test 테스트 labels Dec 1, 2023
@kimscastle kimscastle added this to the 🦁3차 Refactor🦁 milestone Dec 1, 2023
@kimscastle kimscastle self-assigned this Dec 1, 2023
@kimscastle kimscastle merged commit cf035e2 into main Dec 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🦁의성 의성's 🧪Test 테스트
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[TEST] Challenge Unit test
2 participants