XCTesting avec RxSwift

RxBlocking

RxBlocking est l’un des deux frameworks de test disponibles avec RxSwift. Il suit un concept simple: il vous permet de convertir votre flux Observable en BlockingObservable, un observable spéciale qui bloque le thread actuel en attendant des termes spécifiques dictés par ses opérateurs.

Il s’avère utile dans les situations dans lesquelles vous traitez avec une séquence finale – c’est-à-dire qui émet un événement complet ou erroné – ou qui vise à tester un nombre fini d’événements.

RxBlocking fournit plusieurs opérateurs, les plus utiles étant:

toArray (): attend la fin de la séquence et renvoie tous les résultats sous forme de tableau.

first (): attend le premier élément et le retourne.

last (): attend la fin de la séquence et renvoie le dernier élément émis.

func testNumeratorStartsAt4() throws {

  XCTAssertEqual(try viewModel.numeratorText.toBlocking().first(), « 4 »)

  XCTAssertEqual(try viewModel.numeratorValue.toBlocking().first(), 4)

}

func testDenominatorStartsAt4() throws {

  XCTAssertEqual(try viewModel.denominatorText.toBlocking().first(), « 4 »)

}

 

Avantages et inconvénients de RxBlocking

Comme vous l’avez peut-être remarqué, RxBlocking est génial et facile à utiliser car il «enveloppe» les concepts réactifs sous des constructions très connues. Malheureusement, il comporte quelques limitations dont vous devez être conscient:

  • Il vise à tester des séquences finies, ce qui signifie que si vous voulez tester le premier élément ou une liste d’éléments d’une séquence terminée, RxBlocking s’avère très utile. Cependant, dans le cas le plus courant de traitement de séquences non terminantes, l’utilisation de RxBlocking ne vous offre pas la flexibilité dont vous avez besoin.
  • RxBlocking fonctionne en bloquant le thread en cours et en bloquant la run-loop. Si votre Observable programme des événements avec des intervalles ou des délais relativement longs, votre BlockingObservable attend ceux qui se présentent sous une forme synchrone.
  • RxBlocking ne vous aide pas, car il vous permet de définir des événements temporels et de confirmer qu’ils contiennent le bon horodatage, car ils ne capturent que les éléments et non leur heure. Lorsque vous testez des sorties qui dépendent d’une entrée asynchrone,
  •  RxBlocking n’est pas utile car il bloque le thread actuel, par exemple, lors du test d’une sortie nécessitant l’émission d’un autre déclencheur observable.

}

RxTest


RxTest fournit son propre planificateur personnalisé appelé TestScheduler uniquement à des fins de test. Il simplifie le test des événements en fonction du temps en vous permettant de créer des modèles d’observables et d’observateurs afin que vous puissiez «enregistrer» ces événements et les tester.


var scheduler: TestScheduler!
var disposeBag: DisposeBag!
 
scheduler = TestScheduler(initialClock: 0)
disposeBag = DisposeBag()
 
 
func testTappedPlayPauseChangesIsPlaying() {
  // 1
  let isPlaying = scheduler.createObserver(Bool.self)
 
  // 2
  viewModel.isPlaying
    .drive(isPlaying)
    .disposed(by: disposeBag)
 
  // 3
  scheduler.createColdObservable([.next(10, ()),
                                  .next(20, ()),
                                  .next(30, ())])
           .bind(to: viewModel.tappedPlayPause)
           .disposed(by: disposeBag)
 
  // 4
  scheduler.start()
 
  // 5
  XCTAssertEqual(isPlaying.events, [
    .next(0, false),
    .next(10, true),
    .next(20, false),
    .next(30, true)
  ])
}

Utilisez votre TestScheduler pour créer un TestableObserver du type d’éléments que vous voulez simuler – dans ce cas, un Bool.

L’un des principaux avantages de cet observateur spécial est qu’il expose une propriété d’événements que vous pouvez utiliser pour affirmer tout événement ajouté. drive () votre sortie isPlaying dans le nouveau TestableObserver.

C’est là que vous « enregistrez » vos événements. Créez une maquette observable qui imite l’émission de trois “taps” dans l’entrée tappedPlayPause.

Là encore, il s’agit d’un type d’observable spécial appelé TestableObservable, qui utilise votre TestScheduler pour émettre des événements aux heures virtuelles fournies.

Appelez start () sur votre planificateur de test. Cette méthode déclenche les abonnements en attente créés dans les points précédents.

Utilisez une surcharge spéciale de XCTAssertEqual fournie avec RxTest, ce qui vous permet d’affirmer que les événements dans isPlaying sont égaux, en éléments et en temps, à ceux que vous attendez. 10, 20 et 30 correspondent aux heures de déclenchement de vos entrées, et 0 correspond à l’émission initiale de isPlaying.

 
TestableObserver, TestableObservable, Asserting timed events
VirtualTimeUnit conversion with different resolution
numeratorValue = Observable
  .combineLatest(steppedNumerator,
                 maxNumerator.asObservable())
  .map(min)
  .distinctUntilChanged()
  .asDriver(onErrorJustReturn: 0)
Hot Observables Cold observables
… are sequences … are sequences
Use resources (« produce heat ») no matter if there is any observer subscribed. Don’t use resources (don’t produce heat) until observer subscribes.
Variables / properties / constants, tap coordinates, mouse coordinates, UI control values, current time Async operations, HTTP Connections, TCP connections, streams
Usually contains ~ N elements Usually contains ~ 1 element
Sequence elements are produced no matter if there is any observer subscribed. Sequence elements are produced only if there is a subscribed observer.
Sequence computation resources are usually shared between all of the subscribed observers. Sequence computation resources are usually allocated per subscribed observer.
Usually stateful Usually stateless

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *