RxTest - Как сравнить событие <Void>, поскольку Void не Equatable

ViewModel имеет вход (наблюдатель), который привязан к tap событие UIButton в UIViewController. Этот наблюдатель относится к типуAnyObserver<Void>.

В моем модульном тесте я ожидал именно этого:

let correctValues: [Recorded<Event<Void>>] = Recorded.events(
       .next(0, ()),
       .completed(0)
   )

Мое определение тестового наблюдателя:

private var voidEventsObserver: TestableObserver<Void>!

let scheduler = TestScheduler(initialClock: 0)
voidEventsObserver = scheduler.createObserver(Void.self)

Утверждение утверждения:

XCTAssertEqual(voidEventsObserver.events, correctValues)

Я получаю следующую ошибку:

Тип выражения '()' неоднозначен без дополнительного контекста

В Rx, Voidсобытия нормальны, и чтобы правильно протестировать ViewModel, нужно их сравнить. например.next(0, ()), .completed(0) и т.п. Void не является Equatable и нет смысла делать это Equatable. Однако мне нужно подтвердить, что событие.next или .error или .completed. Как я могу утверждать эту часть?

2 ответа

Решение

Работать с Void временами может быть болью.

Поигрался с вашим примером, но добавил условное соответствие к Equatable за Result или Event которые содержат Void невозможно из-за Void не является номинальным типом или из-за того, что эти типы уже имеют противоречивое соответствие Equatable.

Один из подходов - сделать что-то вроде этого:

XCTAssertEqual(voidEventsObserver.events.count, correctValues.count)

for (actual, expected) in zip(voidEventsObserver.events, correctValues) {
    XCTAssertEqual(actual.time, expected.time, "different times")

    let equal: Bool
    switch (actual.value, expected.value) {
    case (.next, .next),
         (.completed, .completed):
        equal = true
    default:
        equal = false
    }
    XCTAssertTrue(equal, "different event")
}

Это чертовски уродливо и трудно читать. Другой подход - ввести оболочку:

struct VoidRecord: Equatable {
    let record: Recorded<Event<Void>>

    static func == (lhs: Self, rhs: Self) -> Bool {
        guard lhs.record.time == rhs.record.time else { return false }

        switch (lhs.record.value, rhs.record.value) {
        case (.next, .next),
             (.completed, .completed):
            return true
        default:
            return false
        }
    }
}

XCTAssertEqual(
    voidEventsObserver.events.map(VoidRecord.init),
    correctValues.map(VoidRecord.init)
)

Это читается намного лучше. Обратите внимание, что это относится к.errorсобытия как всегда разные. Если вам нужно сравнить события ошибок, просто добавьте эту логику из RxSwift в== функция выше.

Это решение, которое сработало для меня. Он заменяет Void эквивалентным VoidValue для Event. Позже он использует XCTAssertEqual, который является частью библиотеки RxSwift, поэтому поведение и вывод будут согласованными.

      func XCTAssertEqual(_ lhs: [Recorded<Event<Void>>], _ rhs: [Recorded<Event<Void>>], file: StaticString = #file, line: UInt = #line) {
    
    struct VoidValue: Equatable {
        static func ==(lhs: Self, rhs: Self) -> Bool {
            return true
        }
    }
    
    let toVoidValueEvent: (Event<Void>) -> Event<VoidValue> = { event in
        switch event {
        case .completed:
            return .completed
        case .error(let e1)
            :
            return .error(e1)
        case .next:
            return .next(.init())
        }
    }
    
    let lhsRecords: [Recorded<Event<VoidValue>>] = lhs.map({ Recorded(time: $0.time, value: toVoidValueEvent($0.value)) })
    let rhsRecords: [Recorded<Event<VoidValue>>] = rhs.map({ Recorded(time: $0.time, value: toVoidValueEvent($0.value)) })
    
    XCTAssertEqual(lhsRecords, rhsRecords)
}
Другие вопросы по тегам