Unit Test

김재형_LittleTale·2025년 10월 20일

Testable

목록 보기
1/2

Unit Test

단위 테스트
특정 유닛이 정상 동작하는지 체크하기 위한 테스트

FIRST

- F ast

  • 여러 테스트를 빠르게 동작하도록

- I solated

  • 독립적으로 진행되도록

- R epeatable

  • 반복적으로 가능하게

- S elf-Automation

  • 자동화

- T imely

  • 수정사항 발생시 수정사항에 대한 여파 확인

Given, When, Then

- Given

테스트 목적의 상황 설명 ex) 3 을 받고 난수 생성

- When

테스트 발생 시점 ex) 버튼 클릭 이벤트

- Then

예상결과, 결과 그 후이 처리


XCTest 메서드들

  • XCTAssertTrue(:) / XCTAssertFalse(:)
XCTAssertTrue(isEnabled)
XCTAssertFalse(items.isEmpty == false)
  • XCTAssertEqual(::) / XCTAssertNotEqual(::)
    (Float/Double은 accuracy: 지원)
XCTAssertEqual(user.name, "Lee")
XCTAssertEqual(distance, 1.0, accuracy: 0.0001)
  • XCTAssertNil(:) / XCTAssertNotNil(:)
XCTAssertNil(cache["missing"])
XCTAssertNotNil(image)
  • XCTAssertGreaterThan(::) / XCTAssertLessThan(::)
    > =, <= 도 각각 제공
XCTAssertGreaterThan(score, 80)
XCTAssertLessThan(latency, 100)
  • XCTAssertIdentical(::) / XCTAssertNotIdentical(::)
    (두 객체 참조가 같은 인스턴스인지)
XCTAssertIdentical(a as AnyObject, b as AnyObject)
  • XCTFail(_:)
    (명시적으로 실패 처리)
if unsupported { XCTFail("미지원 경로") }
  • XCTUnwrap(_:)
    (옵셔널이 nil이면 즉시 실패)
let data = try XCTUnwrap(response.data)

테스트 해보기

NumberHelper (비즈니스 로직)

import Foundation

protocol NumberService: Sendable {
    func fetchNumbers() async throws -> [Int]
    func sumEvenNumbers(_ numbers: [Int]) -> Int
}

extension NumberService {
    func sumEvenNumbers(_ numbers: [Int]) -> Int {
        numbers.filter { $0 % 2 == 0 }.reduce(0, +)
    }
}

enum NumberServiceError: Error, LocalizedError {
    case network
    case malformed

    var errorDescription: String? {
        switch self {
        case .network: return "네트워크 오류가 발생했습니다."
        case .malformed: return "데이터 형식 오류가 발생했습니다."
        }
    }
}

final class TestableHelper: NumberService {
    
    // 임의의 딜레이
    private let delay: UInt64 = 700_000_000
    
    func fetchNumbers() async throws -> [Int] {
        try await Task.sleep(nanoseconds: delay)
        
        return (1...10).map{ _ in Int.random(in: 1...30) }
    }
}

XCTest 타겟 생성 및 연결, 코드

테스트 하고자할 프로젝트나 모듈에 AddTarget 을 진행후
아래와 같이 테스트 해보겠습니다.

import XCTest
@testable import Testable // 현재 프로젝트 ( 만약 Tuist 같은 멀티모듈 환경시 해당 모듈 ).

@MainActor
final class TestableTests: XCTestCase {
    // Test 대상
    private var service: NumberService!
    
    // 테스트에 필요한 설정값
    override func setUpWithError() throws {
        // Put setup code here. This method is called before the invocation of each test method in the class.
        try super.setUpWithError()
        self.service = TestableHelper()
    }
    
    // Test 마치며 정리할 것들 정리 -> 격리성 보장
    override func tearDownWithError() throws {
        try super.tearDownWithError()
        service = nil
    }

    
    /// func fetchNumbers() async throws -> [Int] Test Code
    func testFetchNumber() async throws {
        // given : 필요한 값 세팅
        // -> 현재는 없음
        
        // when : 테스트 코드 실행
        let number = try await service.fetchNumbers()
        
        // then : 결과 비교
        XCTAssertEqual(number.count, 10, "The number Count should be 10")
    }
    
    // sumEvenNumbers(_ numbers: [Int]) -> Int TestCode
    func testSumEvenNumbers() {
        // given None
        
        // when
        let result = service.sumEvenNumbers([1,2,3,4,5,5,6,7,8,9,10,20])
        
        // then
        XCTAssertEqual(result, 50, "The sum of even numbers should be 50")
    }

}

비동기 테스트

expectation(), fulfill(), wait()

  • 충족되거나 시간 초과 까지, 둘 중 먼저 발생하는 시점까지 계속 진행
func testFetchCallBack() throws {
        let manager = FakeNetworkManager(mode: .success([1,2,3,4,5,6]), delay: 100_000) // much smaller
        let exp = expectation(description: "fetch Done") // 뭘 충족할지
        
        manager.fetchNumbers { result in
            let nums = try? result.get()
            XCTAssertEqual(nums, [1,2,3,4,5,6])
            exp.fulfill() // 충족함 
        }
        
        wait(for: [exp], timeout: 2.0)
    }

마무리 하며

오랜만에 다시 글을 올립니다.
개인사정으로 인해... 몇일간 맥북을 못 만지는 상황이 발생해서..!
다음시간에는 UITest 로 찾아 뵙겠습니다. 감사합니다.

profile
IOS 개발자 새싹이, 작은 이야기로부터

0개의 댓글