Reaktív folyamok tesztelése a StepVerifier és a TestPublisher használatával

1. Áttekintés

Ebben az oktatóanyagban alaposan megvizsgáljuk a reaktív folyamok tesztelését StepVerifier és TestPublisher.

Vizsgálatunkat a Tavaszi reaktor reaktor-műveletek láncolatát tartalmazó alkalmazás.

2. Maven-függőségek

A Spring Reactor számos osztályt tartalmaz a reaktív áramok tesztelésére.

Ezeket hozzáadhatjuk a reaktor-teszt függőség:

 io.projectreaktor reaktor-teszt teszt 3.2.3. KIBOCSÁTÁS 

3. StepVerifier

Általánosságban, reaktor-teszt két fő felhasználási lehetősége van:

  • lépésről-lépésre teszt létrehozása StepVerifier
  • előre definiált adatok előállítása a TestPublisher hogy teszteljék a downstream szolgáltatókat

A reaktív folyamok tesztelésénél a leggyakoribb eset az, amikor kiadónk van (a Fényáram vagy Monó), amelyet kódunkban határoztak meg. Szeretnénk tudni, hogyan viselkedik, ha valaki feliratkozik.

A ... val StepVerifier API, meghatározhatjuk a közzétett elemekkel szemben támasztott elvárásainkat milyen elemekre számítunk, és mi történik, amikor az adatfolyamunk befejeződik.

Először hozzunk létre egy kiadót néhány operátorral.

Használjuk a Flux.just (T elemek). Ez a módszer létrehozza a Fényáram amely adott elemeket bocsát ki, majd befejez.

Mivel a haladó operátorok túl vannak a cikk keretein, létrehozunk egy egyszerű kiadót, amely csak négybetűs neveket ad ki nagybetűvel:

Flux forrás = Flux.just ("John", "Monica", "Mark", "Cloe", "Frank", "Casper", "Olivia", "Emily", "Cate"). Szűrő (név -> név .length () == 4) .map (String :: toUpperCase);

3.1. Lépésről-lépésre forgatókönyv

Most teszteljük forrás val vel StepVerifier annak tesztelésére, hogy mi fog történni, ha valaki feliratkozik:

StepVerifier .create (forrás) .expectNext ("JOHN") .expectNextMatches (név -> név.startsWith ("MA")) .expectNext ("CLOE", "CATE") .expectComplete () .verify ();

Először létrehozunk egy StepVerifier építő a teremt módszer.

Ezután becsomagoljuk a sajátunkat Fényáram forrás, amely tesztelés alatt áll. Az első jelet a várhatóNext (T elem), de igazán, tetszőleges számú elemet átadhatunk VárjNext.

Használhatjuk is VárjNextMatches és biztosítja a Állítmány egyediabb mérkőzésre.

Utolsó várakozásunkra azt várjuk, hogy a mi adatfolyamunk teljes lesz.

És végül, használunk igazolni () hogy kiváltsa a tesztünket.

3.2. Kivételek itt StepVerifier

Most összefűzzük a sajátunkat Fényáram kiadó Monó.

Meglesz ez Monó azonnal felmondja hibával, ha előfizet:

Flux hiba = source.concatWith (Mono.error (új IllegalArgumentException ("Üzenetünk")));

Most, négy elem után, arra számítunk, hogy adatfolyamunk kivételével megszűnik:

StepVerifier .create (hiba) .expectNextCount (4) .expectErrorMatches (dobható -> dobható példány az IllegalArgumentException && dobható.getMessage (). Egyenlő ("Üzenetünk")) .verify ();

A kivételek ellenőrzésére csak egy módszert használhatunk. A OnError jel értesíti az előfizetőt arról a kiadó hibaállapotban van bezárva. Ezért később nem fűzhetünk több elvárást.

Ha nem szükséges egyszerre ellenőrizni a kivétel típusát és üzenetét, akkor a dedikált módszerek egyikét használhatjuk:

  • várhatóError () - bármilyen hibára számíthat
  • várhatóError (osztály clazz) – egy adott típusú hibára számíthat
  • VárjErrorMessage (String errorMessage) - hiba várható egy adott üzenetnél
  • várhatóErrorMatches (predikátum predikátum) - várjon egy hibát, amely megfelel egy adott állítmánynak
  • várhatóErrorSatisfies (Consumer assertionConsumer) - fogyasztanak a Dobható egyedi állítás megtétele érdekében

3.3. Időalapú kiadók tesztelése

Néha kiadóink időalapúak.

Tegyük fel például, hogy valós alkalmazásunkban az események között egynapos késés van. Nyilvánvalóan nem akarjuk, hogy tesztjeink egy egész napot lefussanak, hogy ilyen késéssel ellenőrizzék a várható viselkedést.

StepVerifier.withVirtualTime Az építőt úgy tervezték, hogy elkerülje a hosszan tartó teszteket.

Hívással létrehozunk egy építőt withVirtualTime.Vegye figyelembe, hogy ez a módszer nem szükséges Fényárambemenetként. Ehelyett a Támogató, amely lustán létrehozza a tesztelt példányt Fényáram miután beállította az ütemezőt.

Annak bemutatásához, hogy miként tesztelhetjük az események közötti várható késést, hozzunk létre egy Fényáram egy másodperc intervallummal, amely két másodpercig tart. Ha az időzítő megfelelően működik, csak két elemet kell kapnunk:

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

Ne feledje, hogy kerülnünk kell a Fényáram korábban a kódban, majd miután a Támogató ezt a változót adja vissza. Helyette, mindig példáznunk kell Fényáram a lambda belsejében.

Két fő várakozási módszer foglalkozik az idővel:

  • thenAwait (Időtartam) - szünetelteti a lépések értékelését; új események fordulhatnak elő ez idő alatt
  • várhatóNoEvent (Időtartam) - nem sikerül, ha a időtartamát; a szekvencia átmegy egy adottval időtartamát

Kérjük, vegye figyelembe, hogy az első jel az előfizetéses esemény, tehát minden várhatóNoEvent (Időtartam) előtt kell lennie várhatóSubscription ().

3.4. Végrehajtás utáni állítások StepVerifier

Tehát, amint láttuk, egyszerű leírni elvárásainkat lépésről lépésre.

Azonban, néha további állapotot kell ellenőriznünk, miután az egész forgatókönyv sikeresen lejátszódott.

Hozzunk létre egy egyedi kiadót. Kibocsát néhány elemet, majd befejezi, szünetelteti és kiadja még egy elemet, amelyet eldobunk:

Fluxforrás = Flux.create (emitter -> {emitter.next (1); emitter.next (2); emitter.next (3); emitter.complete (); próbáld ki a {Thread.sleep (1000);} catch ( InterruptedException e) {e.printStackTrace ();} emitter.next (4);}). Szűrő (szám -> szám% 2 == 0);

Arra számítunk, hogy 2-t bocsát ki, de 4-et dob, mivel hívtunk emitter.comteljes első.

Tehát ellenőrizzük ezt a viselkedést a használatával VerifyThenAssertThat. Ez a módszer visszatér StepVerifier. Állítások amelyhez hozzáadhatjuk állításainkat:

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

4. Adatok előállítása a TestPublisher

Előfordulhat, hogy szükségünk van néhány speciális adatra a kiválasztott jelek kiváltásához.

Például nagyon különleges helyzetünk lehet, amelyet tesztelni akarunk.

Alternatív megoldásként dönthetünk úgy, hogy bevezetjük saját operátorunkat, és szeretnénk kipróbálni, hogyan viselkedik.

Mindkét esetben használhatjuk TestPublisher, melyik lehetővé teszi számunkra a különféle jelek programozott kiváltását:

  • következő (T érték) vagy következő (T érték, T nyugalom) - küldjön egy vagy több jelet az előfizetőknek
  • kibocsátás (T érték) - ugyanaz, mint a következő (T) hanem felszólít teljes() később
  • teljes() - megszakít egy forrást a teljes jel
  • hiba (dobható tr) - hibával szünteti meg a forrást
  • fluxus () - kényelmes módszer a TestPublisher -ba Fényáram
  • monó() - ugyanaz, mint mi fényáram() de átöleli a Monó

4.1. A. Létrehozása TestPublisher

Hozzunk létre egy egyszerű TestPublisher amely néhány jelet bocsát ki, majd egy kivétellel megszűnik:

TestPublisher .create () .next ("Első", "Második", "Harmadik") .hiba (új RuntimeException ("Üzenet"));

4.2. TestPublisher akcióban

Mint korábban említettük, néha előfordulhat, hogy kiváltjuk finoman megválasztott jel, amely szorosan illeszkedik egy adott helyzethez.

Ebben az esetben különösen fontos, hogy teljes mértékben elsajátítsuk az adatok forrását. Ennek elérése érdekében ismét támaszkodhatunk TestPublisher.

Először hozzunk létre egy osztályt, amely használja Fényáram mint konstruktor paraméter a művelet végrehajtására getUpperCase ():

class UppercaseConverter {private final Flux source; UppercaseConverter (Flux forrás) {this.source = forrás; } Flux getUpperCase () {visszatérési forrás .map (String :: toUpperCase); }}

Feltételezem, hogy UppercaseConverter osztályunk összetett logikával és operátorokkal rendelkezik, és nagyon konkrét adatokat kell szolgáltatnunk a forrás kiadó.

Ezt könnyen elérhetjük TestPublisher:

végső TestPublisher testPublisher = TestPublisher.create (); UppercaseConverter uppercaseConverter = új UppercaseConverter (testPublisher.flux ()); StepVerifier.create (uppercaseConverter.getUpperCase ()). Akkor (() -> testPublisher.emit ("aA", "bb", "ccc")) .expectNext ("AA", "BB", "CCC"). verComplete ();

Ebben a példában létrehozunk egy tesztet Fényáram kiadó a UppercaseConverter konstruktor paraméter. Akkor, a mi TestPublisher három elemet bocsát ki és teljesít.

4.3. Viselkedés TestPublisher

Másrészről, rossz viselkedést hozhatunk létre TestPublisher val vel a createNonCompliant gyári módszer. Át kell adnunk a konstruktorban egy enum értéket TestPublisher.Sértés. Ezek az értékek meghatározzák, hogy a specifikációk mely részeit hagyhatja figyelmen kívül kiadónk.

Vessünk egy pillantást a TestPublisher ez nem dobja el a NullPointerException a nulla elem:

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

Továbbá ALLOW_NULL, használhatjuk is TestPublisher.Sértés nak nek:

  • REQUEST_OVERFLOW - lehetővé teszi a hívást következő() anélkül, hogy egy IllegalStateException ha nincs elegendő számú kérés
  • CLEANUP_ON_TERMINATE - lehetővé teszi bármilyen befejező jel küldését egymás után többször
  • DEFER_CANCELLATION - lehetővé teszi számunkra, hogy figyelmen kívül hagyjuk a törlési jeleket és folytassuk az elemek kibocsátását

5. Következtetés

Ebben a cikkben, megvitattuk a reaktív áramok tesztelésének különféle módszereit Tavaszi reaktor projekt.

Először láttuk, hogyan kell használni StepVerifier a kiadók tesztelésére. Aztán láttuk, hogyan kell használni TestPublisher. Hasonlóképpen láttuk, hogyan kell működni a nem megfelelő viselkedéssel TestPublisher.

Szokás szerint minden példánk megvalósítása megtalálható a Github projektben.