Introduktion til afventning

1. Introduktion

Et almindeligt problem med asynkrone systemer er, at det er svært at skrive læsbare tests til dem, der er fokuseret på forretningslogik og ikke er forurenet med synkroniseringer, timeouts og samtidighedskontrol.

I denne artikel skal vi se på Awaitility - et bibliotek, der giver et simpelt domænespecifikt sprog (DSL) til test af asynkrone systemer.

Med Awaitility kan vi udtrykke vores forventninger fra systemet i en letlæselig DSL.

2. Afhængigheder

Vi er nødt til at tilføje afventningsafhængigheder til vores pom.xml.

Det ventetid biblioteket vil være tilstrækkeligt til de fleste brugssager. Hvis vi vil bruge proxybaserede betingelser, vi er også nødt til at levere ventetid-proxy bibliotek:

 org.awaitility awaitility 3.0.0 test org.awaitility awaitility-proxy 3.0.0 test 

Du kan finde den nyeste version af ventetid og ventetid-proxy biblioteker på Maven Central.

3. Oprettelse af en asynkron tjeneste

Lad os skrive en simpel asynkron tjeneste og teste den:

offentlig klasse AsyncService {privat final int DELAY = 1000; privat afsluttende int INIT_DELAY = 2000; privat AtomicLong-værdi = ny AtomicLong (0); private Executor executor = Executors.newFixedThreadPool (4); privat flygtig boolsk initialiseret = falsk; ugyld initialiseret () {executor.execute (() -> {sleep (INIT_DELAY); initialiseret = sand;}); } boolsk isInitialized () {return initialiseret; } ugyldig addValue (lang val) {throwIfNotInitialized (); executor.execute (() -> {sleep (DELAY); value.addAndGet (val);}); } offentlig lang getValue () {throwIfNotInitialized (); return value.longValue (); } privat ugyldig søvn (int forsinkelse) {prøv {Thread.sleep (forsinkelse); } fange (InterruptedException e) {}} private void throwIfNotInitialized () {if (! initialized) {throw new IllegalStateException ("Service is not initialized"); }}}

4. Test med ventetid

Lad os nu oprette testklassen:

offentlig klasse AsyncServiceLongRunningManualTest {private AsyncService asyncService; @Før offentlig ugyldig setUp () {asyncService = ny AsyncService (); } // ...}

Vores test kontrollerer, om initialisering af vores service finder sted inden for en bestemt timeout-periode (standard 10s) efter opkald til initialisere metode.

Denne testtilstand venter blot på, at tjenesteinitialiseringstilstanden ændres eller kaster en ConditionTimeoutException hvis tilstandsændringen ikke sker.

Status opnås ved en Kan kaldes der afstemmer vores service med definerede intervaller (100 ms standard) efter en specificeret indledende forsinkelse (standard 100 ms). Her bruger vi standardindstillingerne for timeout, interval og forsinkelse:

asyncService.initialize (); afvente () .until (asyncService :: isInitialized);

Her bruger vi vente - en af ​​de statiske metoder til Ventetid klasse. Det returnerer en forekomst af en Tilstandsfabrik klasse. Vi kan også bruge andre metoder som f.eks givet af hensyn til øget læsbarhed.

Standard timingparametrene kan ændres ved hjælp af statiske metoder fra Ventetid klasse:

Awaitility.setDefaultPollInterval (10, TimeUnit.MILLISECONDS); Awaitility.setDefaultPollDelay (Duration.ZERO); Awaitility.setDefaultTimeout (Varighed.ONE_MINUTE);

Her kan vi se brugen af Varighed klasse, som giver nyttige konstanter til de hyppigst anvendte tidsperioder.

Det kan vi også give tilpassede timingværdier for hver vente opkald. Her forventer vi, at initialisering højst finder sted efter fem sekunder og mindst efter 100 ms med afstemningsintervaller på 100 ms:

asyncService.initialize (); afventer () .atLeast (Duration.ONE_HUNDRED_MILLISECONDS) .atMost (Duration.FIVE_SECONDS) .with () .pollInterval (Duration.ONE_HUNDRED_MILLISECONDS) .until (asyncService :: isInitialized);

Det er værd at nævne, at Tilstandsfabrik indeholder yderligere metoder som med, derefter, og, givet. Disse metoder gør ikke noget og vender bare tilbage det her, men de kunne være nyttige til at forbedre læsbarheden af ​​testbetingelser.

5. Brug af Matchers

Ventetiden tillader også brugen af hamcrest matchere for at kontrollere resultatet af et udtryk. For eksempel kan vi kontrollere, at vores lang værdi ændres som forventet efter opkald til Tilføj værdi metode:

asyncService.initialize (); afvente () .until (asyncService :: isInitialized); lang værdi = 5; asyncService.addValue (værdi); afvente () .until (asyncService :: getValue, equalTo (værdi));

Bemærk, at vi i dette eksempel brugte den første vente ring for at vente, indtil tjenesten er initialiseret. Ellers er getValue metode ville kaste en IllegalStateException.

6. Ignorer undtagelser

Nogle gange har vi en situation, hvor en metode kaster en undtagelse, før et asynkront job udføres. I vores tjeneste kan det være et opkald til getValue metode, før tjenesten initialiseres.

Awaitility giver mulighed for at ignorere denne undtagelse uden at mislykkes i en test.

Lad os f.eks. Kontrollere, at getValue resultatet er lig med nul lige efter initialisering og ignorerer IllegalStateException:

asyncService.initialize (); given (). ignoreException (IllegalStateException.class) .await (). atMost (Duration.FIVE_SECONDS) .atLeast (Duration.FIVE_HUNDRED_MILLISECONDS) .until (asyncService :: getValue, equalTo (0L));

7. Brug af proxy

Som beskrevet i afsnit 2 skal vi medtage ventetid-proxy at bruge proxy-baserede betingelser. Ideen med proxying er at give ægte metodeopkald til betingelser uden implementering af en Kan kaldes eller lambda-udtryk.

Lad os bruge AwaitilityClassProxy.to statisk metode til at kontrollere det AsyncService initialiseres:

asyncService.initialize (); afvente () .untilCall (til (asyncService) .isInitialized (), equalTo (true));

8. Adgang til felter

Awaitility kan endda få adgang til private felter for at udføre påstande om dem. I det følgende eksempel kan vi se en anden måde at få initialiseringsstatus for vores service på:

asyncService.initialize (); afventer () .until (fieldIn (asyncService) .ofType (boolean.class) .andWithName ("initialized"), equalTo (true));

9. Konklusion

I denne hurtige vejledning introducerede vi Awaitility-biblioteket, blev bekendt med dets grundlæggende DSL til test af asynkrone systemer og så nogle avancerede funktioner, der gør biblioteket fleksibelt og let at bruge i virkelige projekter.

Som altid er alle kodeeksempler tilgængelige på Github.


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