✅ 왜 RxBlocking이 필요할까?
RxSwift는 비동기 스트림을 다루는 라이브러리입니다. 하지만 테스트는 동기적으로 결과를 확인해야 합니다
// ❌ 이렇게 하면 안 됨 - 비동기라서 테스트가 끝나기 전에 완료됨
func test_잘못된_방법() {
let result = userRepository.getUser(id: 1) // Single<User>
// result는 아직 실행되지 않은 스트림일 뿐!
#expect(result == expectedUser) // 컴파일 에러!
}
// ✅ RxBlocking으로 동기화
func test_올바른_방법() throws {
let result = try userRepository.getUser(id: 1)
.toBlocking() // 비동기 → 동기 변환
.single() // 결과 추출
#expect(result.name == "홍길동")
}
✅ Swift Testing vs XCTest 비교
import XCTest
class UserRepositoryTests: XCTestCase {
var sut: UserRepository!
override func setUp() {
sut = UserRepository()
}
func testGetUser() {
// Given
let userId = 1
// When
let result = try! sut.getUser(id: userId).toBlocking().single()
// Then
XCTAssertEqual(result.name, "양밀루")
XCTAssertEqual(result.age, 472)
}
}
import Testing
@Suite("사용자 Repository 테스트")
struct UserRepositoryTests {
@Test("사용자 조회가 성공하는지 테스트")
func getUser_성공() throws {
// Given
let repository = UserRepository()
let userId = 1
// When
let result = try repository.getUser(id: userId)
.toBlocking()
.single()
// Then
#expect(result.name == "양밀루")
#expect(result.age == 472)
}
}
주요 차이점:
- class → struct
- XCTAssert* → #expect
- func test* → @Test
- setUp/tearDown 불필요 (struct라서)
✅ RxBlocking 메서드들
1. single() - 하나의 값을 기대할 때
// API 호출 결과 하나 받기
let user = try repository.getUser(id: 1)
.toBlocking()
.single() // Single<User> → User
2. first() - 첫 번째 값만 필요할 때
// 스트림에서 첫 번째 값만
let firstMessage = try chatRepository.getMessages()
.toBlocking()
.first() // Observable<[Message]> → [Message]?
3. toArray() - 모든 값을 배열로
// 모든 이벤트를 배열로 수집
let allEvents = try eventStream
.take(3) // 3개만 받고
.toBlocking()
.toArray() // [Event]
실제 활용 예시
// 💡 타임아웃 설정 (느린 테스트 방지)
let result = try service.getData()
.toBlocking(timeout: 5.0) // 5초 타임아웃
.single()
// 💡 에러 타입도 정확히 검증
#expect(throws: NetworkError.self) {
try service.failingMethod().toBlocking().single()
}
// 💡 옵셔널 처리
let maybeResult = try service.getMaybeData()
.toBlocking()
.single()
if let result = maybeResult {
#expect(result.isValid)
} else {
#expect(Bool(false), "결과가 nil이면 안 됨")
}
'iOS' 카테고리의 다른 글
[iOS] Factory Pattern (3) | 2025.07.24 |
---|---|
[iOS] Coordinator 패턴 적용기 (0) | 2025.07.22 |
[트러블슈팅] UICollectionViewCell 그림자가 초기 렌더링 시 안 보이는 문제 (0) | 2025.07.18 |
[iOS] Coordinator 패턴으로 화면 전환 로직 분리하기 (0) | 2025.07.10 |
[iOS] 클로저에서 async/await로 리팩토링하기 (1) | 2025.07.08 |