Как мы можем протестировать класс, который генерирует случайные состояния и который не может генерировать одни и те же состояния дважды?

У нас есть три состояния. Как мы можем протестировать (с помощью модульных тестов) наш класс, который генерирует случайное состояние каждые 5 секунд и который не может генерировать одно и то же состояние дважды подряд? Код нашего класса генератора случайных чисел находится ниже `final class StateRandomGenerator: RandomGeneratorProtocol {private var sourceObservable: Disposable? private (set) var previousValue: Int?var generatedValue: PublishSubject = PublishSubject()

      init(_ interval: RxTimeInterval,_ scheduler: SchedulerType = MainScheduler.instance) {
    sourceObservable = Observable<Int>
        .interval(interval, scheduler: scheduler)
        .flatMap { [unowned self] _ in self.generateRandom()}
        .compactMap { state in
            return state?.description
        }
        .subscribe(onNext: { [weak self] description in
            self?.generatedValue.onNext(description)
        })
}
func generateRandom() -> Observable<ConnectionState?> {
    return Observable.create { [weak self] observer  in
        var randomNumber = Int.random(in: 0..<ConnectionState.count)
        guard let previousValue = self?.previousValue else {
            let value = ConnectionState(rawValue: randomNumber)
            self?.previousValue = randomNumber
            observer.onNext(value)
            return Disposables.create()
        }
        while randomNumber == previousValue {
            randomNumber = Int.random(in: 0..<ConnectionState.count)
        }
        self?.previousValue = randomNumber
        let value = ConnectionState(rawValue: randomNumber)
        observer.onNext(value)
        
        return Disposables.create()
    }
}
enum ConnectionState: Int {
case error
case connecting
case established

var description: String {
    switch self {
    case .connecting:
        return "It is connecting"
    case .error:
        return "There is an error"
    case .established:
        return "Thе connection is established"
    }
}

} `

1 ответ

Вы не можете успешно протестировать свой класс, потому что он не останавливается. Он просто блокирует процессор и поглощает память до тех пор, пока система, наконец, не истощится и не выйдет из строя.

Ниже приведен рабочий и протестированный Observable, который делает то, что вы хотите ... Тест создает 100000 ConnectionStates, а затем проверяет, что никакие два соседних объекта не идентичны.

Основная логика функции - закрытие, передаваемое в compactMap который захватывает все случаи, отфильтровывает предыдущий случай, а затем выбирает случайный элемент из остатка.

Было бы довольно легко сделать это универсальным для любого перечисления, которое я ожидаю. Я оставлю это читателю в качестве упражнения.

      func stateRandom(_ interval: RxTimeInterval,_ scheduler: SchedulerType = MainScheduler.instance) -> Observable<ConnectionState> {
    Observable.deferred {
        var previous: ConnectionState? = nil
        return Observable<Int>.interval(interval, scheduler: scheduler)
            .compactMap { _ in
                ConnectionState.allCases
                    .filter { $0 != previous }
                    .randomElement()
            }
            .do(onNext: { previous = $0 })
    }
}

enum ConnectionState: CaseIterable, Equatable {
    case error
    case connecting
    case established
}

class rx_sandboxTests: XCTestCase {

    func test() throws {
        let scheduler = TestScheduler(initialClock: 0)

        let result = scheduler.start { stateRandom(.seconds(1), scheduler).take(100000) }

        for (prev, current) in zip(result.events, result.events.dropFirst()) {
            XCTAssertNotEqual(prev.value, current.value)
        }
    }
}

Не фанат нечистоты закрытия, но работает ...

Другие вопросы по тегам