Testovanie reaktívnych prúdov pomocou StepVerifier a TestPublisher

1. Prehľad

V tomto tutoriáli sa podrobne pozrieme na testovanie reaktívnych prúdov pomocou StepVerifier a TestPublisher.

Pri vyšetrovaní budeme vychádzať z Jarný reaktor aplikácia obsahujúca reťaz reaktorových operácií.

2. Maven závislosti

Spring Reactor prichádza s niekoľkými triedami pre testovanie reaktívnych prúdov.

Tieto môžeme získať pridaním reaktorový test závislosť:

 io.projectreactor reactor-test test 3.2.3. UVOĽNENIE 

3. StepVerifier

Všeobecne, reaktorový test má dve hlavné použitia:

  • vytvorenie podrobného testu s StepVerifier
  • produkovanie preddefinovaných údajov s TestPublisher následných operátorov

Najbežnejším prípadom pri testovaní reaktívnych prúdov je, keď máme vydavateľa (a Flux alebo Mono) definované v našom kóde. Chceme vedieť, ako sa správa, keď sa niekto prihlási na odber.

Vďaka StepVerifier API, môžeme definovať naše očakávania od zverejnených prvkov v zmysle aké prvky očakávame a čo sa stane, keď sa náš stream dokončí.

Najskôr si vytvorme vydavateľa s niektorými operátormi.

Použijeme a Flux.just (T prvky). Táto metóda vytvorí a Flux ktorý vyžaruje dané prvky a potom sa dokončí.

Pretože pokročilé operátory presahujú rámec tohto článku, vytvoríme iba jednoduchého vydavateľa, ktorý vydáva iba štvorpísmenové názvy mapované na veľké písmená:

Zdroj toku = Flux.just („John“, „Monica“, „Mark“, „Cloe“, „Frank“, „Casper“, „Olivia“, „Emily“, „Cate“) .filter (meno -> meno .length () == 4) .map (String :: toUpperCase);

3.1. Podrobný scenár

Poďme si teda otestovať našu zdroj s StepVerifier aby sme otestovali, čo sa stane, keď sa niekto prihlási na odber:

StepVerifier .create (zdroj) .expectNext ("JOHN") .expectNextMatches (name -> name.startsWith ("MA")) .expectNext ("CLOE", "CATE") .expectComplete () .verify ();

Najskôr vytvoríme a StepVerifier staviteľ s vytvoriť metóda.

Ďalej zabalíme naše Flux zdroj, ktorý je v teste. Prvý signál je overený pomocou expectNext (prvok T), ale naozaj, môžeme odovzdať ľubovoľný počet prvkov expectNext.

Môžeme tiež použiť expectNextMatches a poskytnúť a Predikát pre prispôsobenejšiu zhodu.

Podľa nášho posledného očakávania očakávame, že sa náš stream dokončí.

A nakoniec, používame overiť () aby sme spustili náš test.

3.2. Výnimky v StepVerifier

Teraz spojme naše Flux vydavateľ s Mono.

Toto budeme mať Mono pri prihlásení na odber okamžite ukončiť s chybou:

Chyba toku = source.concatWith (Mono.error (nový IllegalArgumentException ("naša správa")));

Teraz, po štyroch všetkých prvkoch, očakávame, že náš stream bude ukončený s výnimkou:

StepVerifier .create (chyba) .expectNextCount (4) .expectErrorMatches (hádzateľná -> hádzateľná inštancia IllegalArgumentException && throwable.getMessage (). Equals ("Naša správa")) .verify ();

Na overenie výnimiek môžeme použiť iba jednu metódu. The OnError signál oznamuje predplatiteľovi, že vydavateľ je uzavretý s chybovým stavom. Preto už nemôžeme pridať ďalšie očakávania.

Ak nie je potrebné skontrolovať typ a správu výnimky naraz, môžeme použiť jednu z vyhradených metód:

  • expectError () - očakávajte akýkoľvek druh chyby
  • expectError (trieda clazz) – očakávajte chybu konkrétneho typu
  • expectErrorMessage (reťazec errorMessage) - očakávajte chybu s konkrétnou správou
  • expectErrorMatches (predikát predikát) - očakávajte chybu, ktorá sa zhoduje s daným predikátom
  • expectErrorSatisfies (Consumer assertionConsumer) - konzumovať a Hoditeľné za účelom vykonania vlastného tvrdenia

3.3. Testovanie časovo orientovaných vydavateľov

Naši vydavatelia niekedy fungujú na základe času.

Predpokladajme napríklad, že v našej aplikácii v reálnom živote medzi udalosťami máme jednodňové oneskorenie. Teraz samozrejme nechceme, aby naše testy bežali celý deň, aby sme overili očakávané správanie s takým oneskorením.

StepVerifier.withVirtualTime builder je navrhnutý tak, aby sa vyhol dlhodobým testom.

Vytvoríme staviteľa volaním withVirtualTime.Upozorňujeme, že táto metóda nezaberá Fluxako vstup. Namiesto toho to vyžaduje a Dodávateľ, ktorý lenivo vytvára inštanciu testovaného Flux po nastavení plánovača.

Aby sme demonštrovali, ako môžeme testovať očakávané oneskorenie medzi udalosťami, vytvorme a Flux s intervalom jednej sekundy, ktorý trvá dve sekundy. Ak časovač beží správne, mali by sme dostať iba dva prvky:

StepVerifier .withVirtualTime (() -> Flux.interval (Duration.ofSeconds (1)). Take (2)) .expectSubscription () .expectNoEvent (Duration.ofSeconds (1)) .expectNext (0L) .thenAwait (Duration.ofSeconds) (1)) .expectNext (1L) .verifyComplete ();

Upozorňujeme, že by sme sa mali vyhnúť inštancii Flux skôr v kóde a potom s Dodávateľ vrátenie tejto premennej. Namiesto toho mali by sme vždy vytvoriť inštanciu Flux vo vnútri lambda.

Existujú dve hlavné metódy očakávania, ktoré sa zaoberajú časom:

  • thenAwait (doba trvania) - pozastaví hodnotenie krokov; počas tejto doby môžu nastať nové udalosti
  • expectNoEvent (doba trvania) - zlyhá, keď sa vyskytne akákoľvek udalosť počas trvanie; sekvencia prejde daným trvanie

Upozorňujeme, že prvým signálom je udalosť predplatného, ​​takže každý expectNoEvent (doba trvania) by malo predchádzať expectSubscription ().

3.4. Následné tvrdenia s StepVerifier

Ako sme teda videli, je jednoduché popísať naše očakávania krok za krokom.

Avšak niekedy musíme úspešne overiť ďalší stav po úspešnom odohraní celého nášho scenára.

Vytvorme si vlastného vydavateľa. Bude vydávať niekoľko prvkov, potom dokončí, pozastaví a vydá ešte jeden prvok, ktorý vypustí:

Flux source = Flux.create (emitter -> {emitter.next (1); emitter.next (2); emitter.next (3); emitter.complete (); try {Thread.sleep (1000);} catch ( InterruptedException e) {e.printStackTrace ();} emitter.next (4);}). Filter (number -> number% 2 == 0);

Očakávame, že bude emitovať 2, ale klesne 4, odkedy sme volali emitter.complete najprv.

Overme si teda toto správanie pomocou verifyThenAssertThat. Táto metóda sa vráti StepVerifier. Tvrdenia na ktoré môžeme pridať naše tvrdenia:

@Test public void dropsElements () {StepVerifier.create (source) .expectNext (2) .expectComplete () .verifyThenAssertThat () .hasDropped (4) .tookLessThan (Duration.ofMillis (1050)); }

4. Produkcia dát s TestPublisher

Niekedy môžeme potrebovať nejaké špeciálne údaje, aby sme spustili vybrané signály.

Napríklad môžeme mať veľmi konkrétnu situáciu, ktorú chceme vyskúšať.

Prípadne sa môžeme rozhodnúť implementovať vlastného operátora a chcieť otestovať, ako sa správa.

Pre oba prípady môžeme použiť TestPublisher, ktoré umožňuje nám programovo spúšťať rôzne signály:

  • nasledujúci (hodnota T) alebo nasledujúci (hodnota T, zvyšok T) - poslať jeden alebo viac signálov predplatiteľom
  • emitovať (hodnota T) - rovnaké ako nasledujúce T) ale vzýva kompletný () potom
  • kompletný () - ukončí zdroj s kompletný signál
  • error (Throwable tr) - ukončí zdroj s chybou
  • tok () - pohodlná metóda zabalenia a TestPublisher do Flux
  • mono () - rovnakí my tok () ale zavinuje do a Mono

4.1. Vytvorenie a TestPublisher

Vytvorme jednoduchý TestPublisher ktorý vysiela niekoľko signálov a potom končí s výnimkou:

TestPublisher .create () .next ("Prvý", "Druhý", "Tretí"). Chyba (nová RuntimeException ("Správa"));

4.2. TestPublisher v akcii

Ako sme už spomínali, niekedy môžeme chcieť spustiť jemne vybraný signál, ktorý úzko zodpovedá konkrétnej situácii.

Teraz je v tomto prípade obzvlášť dôležité, že máme úplné zvládnutie zdroja údajov. Na dosiahnutie tohto cieľa sa môžeme opäť spoľahnúť TestPublisher.

Najskôr si vytvorme triedu, ktorá používa Flux ako parameter konštruktora na vykonanie operácie getUpperCase ():

trieda UppercaseConverter {súkromný konečný zdroj toku; UppercaseConverter (zdroj toku) {this.source = zdroj; } Flux getUpperCase () {návratový zdroj .map (String :: toUpperCase); }}

Predpokladajme, že Konvertor veľkých písmen je naša trieda so zložitou logikou a operátormi a musíme dodávať veľmi konkrétne údaje z zdroj vydavateľ.

Ľahko to dosiahneme pomocou TestPublisher:

final TestPublisher testPublisher = TestPublisher.create (); UppercaseConverter uppercaseConverter = nový UppercaseConverter (testPublisher.flux ()); StepVerifier.create (uppercaseConverter.getUpperCase ()). Potom (() -> testPublisher.emit ("aA", "bb", "ccc")) .expectNext ("AA", "BB", "CCC"). verifyComplete ();

V tomto príklade vytvoríme test Flux vydavateľ v Konvertor veľkých písmen parameter konštruktora. Potom, náš TestPublisher vydáva tri prvky a je kompletný.

4.3. Nesprávne správanie TestPublisher

Na druhej strane, môžeme vytvoriť zlé správanie TestPublisher s createNonCompliant továrenská metóda. Musíme vložiť do konštruktora jednu z hodnôt enum TestPublisher. Porušenie. Tieto hodnoty určujú, ktoré časti špecifikácií môže náš vydavateľ prehliadnuť.

Poďme sa pozrieť na a TestPublisher to nebude hádzať a NullPointerException pre nulový element:

TestPublisher .createNoncompliant (TestPublisher.Violation.ALLOW_NULL) .emit ("1", "2", null, "3"); 

Okrem tohoto ALLOW_NULL, môžeme tiež použiť TestPublisher. Porušenie do:

  • REQUEST_OVERFLOW - umožňuje volanie Ďalšie() bez hádzania IllegalStateException keď je nedostatočný počet požiadaviek
  • CLEANUP_ON_TERMINATE - umožňuje posielať akýkoľvek ukončovací signál niekoľkokrát za sebou
  • DEFER_CANCELLATION - umožňuje ignorovať signály zrušenia a pokračovať v emitovaní prvkov

5. Záver

V tomto článku diskutovali sme o rôznych spôsoboch testovania reaktívnych prúdov z Jarný reaktor projekt.

Najprv sme videli, ako sa používa StepVerifier otestovať vydavateľov. Potom sme videli, ako sa používa TestPublisher. Podobne sme videli, ako pracovať so zlým správaním TestPublisher.

Ako obvykle, implementáciu všetkých našich príkladov nájdete v projekte Github.


$config[zx-auto] not found$config[zx-overlay] not found