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)
}