We have three states.How can we test(with unit tests) our class which generates random state every 5 seconds, and which can not generate the same state twice in a row? The code of our random generator class is below ` 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"
}
}
} `
You can't successfully unit test your class because it doesn't halt. It just pegs the CPU and chews up memory until the system is finally starved and crashes.
Below is a working and tested Observable that does what you want... The test creates 100,000 ConnectionStates and then checks to ensure that no two adjacent are identical.
The main logic of the function is the closure passed to
map
which grabs all the cases and filters out the previous case. A random element is chosen from the remainder.It would be pretty easy to make this generic across any enum I expect. I'll leave that as an exercise for the reader.