Test af en REST API med JBehave

1. Introduktion

I denne artikel vil vi hurtigt se på JBehave og derefter fokusere på at teste en REST API fra et BDD-perspektiv.

2. JBehave og BDD

JBehave er en Behavior Driven Development-ramme. Det har til hensigt at give en intuitiv og tilgængelig måde til automatiseret accepttest.

Hvis du ikke er fortrolig med BDD, er det en god ide at starte med denne artikel, der dækker en anden BDD-testramme - Agurk, hvor vi introducerer den generelle BDD-struktur og funktioner.

I lighed med andre BDD-rammer vedtager JBehave følgende koncepter:

  • Story - repræsenterer en automatisk eksekverbar forøgelse af forretningsfunktionalitet, omfatter et eller flere scenarier
  • Scenarier - repræsenterer konkrete eksempler på systemets opførsel
  • Trin - repræsenterer faktisk opførsel ved hjælp af klassiske BDD-nøgleord: Givet, Hvornår og Derefter

Et typisk scenario ville være:

Givet en forudsætning Når en begivenhed finder sted, skal resultatet fanges

Hvert trin i scenariet svarer til en kommentar i JBehave:

  • @Given: indled konteksten
  • @Hvornår: gør handlingen
  • @Derefter: test det forventede resultat

3. Maven-afhængighed

For at gøre brug af JBehave i vores maven-projekt, skal jbehave-kerneafhængighed inkluderes i pom:

 org.jbehave jbehave-core 4.1 test 

4. Et hurtigt eksempel

For at bruge JBehave skal vi følge følgende trin:

  1. Skriv en brugerhistorie
  2. Kortlæg trin fra brugerhistorien til Java-kode
  3. Konfigurer brugerhistorier
  4. Kør JBehave-tests
  5. Gennemgå resultater

4.1. Historie

Lad os starte med følgende enkle historie: “Som bruger vil jeg øge en tæller, så jeg kan få tællerens værdi til at stige med 1”.

Vi kan definere historien i en .historie fil:

Scenarie: når en bruger øger en tæller, øges dens værdi med 1 givet en tæller Og tælleren har en hvilken som helst integreret værdi Når brugeren øger tælleren, skal tællerens værdi være 1 større end tidligere værdi

4.2. Kortlægningstrin

I betragtning af trinnene, lad os implementere dette i Java:

offentlig klasse IncreaseSteps {privat int-tæller; private int previousValue; @Given ("en tæller") offentlig ugyldighed aCounter () {} @Given ("tælleren har en integreret værdi") offentlig ugyldig tællerHasAnyIntegralValue () {tæller = ny tilfældig (). NextInt (); previousValue = tæller; } @Når ("brugeren øger tælleren") offentlig tomrum øgerTheCounter () {tæller ++; } @Then ("værdien af ​​tælleren skal være 1 større end den forrige værdi") offentlig ugyldigValueOfTheCounterMustBe1Greater () {assertTrue (1 == tæller - forrigeValue); }}

Huske på, at værdien i kommentarerne skal stemme overens med beskrivelsen.

4.3. Konfiguration af vores historie

For at udføre trinnene er vi nødt til at oprette scenen for vores historie:

offentlig klasse IncreaseStoryLiveTest udvider JUnitStories {@Override offentlig konfigurationskonfiguration () {returner ny MostUsefulConfiguration () .useStoryLoader (ny LoadFromClasspath (this.getClass ())) .useStoryReporterBuilder (ny StoryReporterBuilder () .withLocationClassLocation ) .withFormats (CONSOLE)); } @ Override public InjectableStepsFactory stepsFactory () {returner ny InstanceStepsFactory (konfiguration (), ny IncreaseSteps ()); } @ Override beskyttet List storyPaths () {return Arrays.asList ("increase.story"); }}

I storyPaths (), vi leverer vores .historie filsti, der skal analyseres af JBehave. Faktiske trinimplementering findes i trinFabrik (). Så ind konfiguration (), story loader og story report er korrekt konfigureret.

Nu hvor vi har alt klar, kan vi begynde vores historie ved blot at køre: mvn ren test.

4.4. Gennemgang af testresultater

Vi kan se vores testresultat i konsollen. Da vores tests er bestået med succes, ville outputen være den samme med vores historie:

Scenarie: når en bruger øger en tæller, øges dens værdi med 1 givet en tæller Og tælleren har en integreret værdi Når brugeren øger tælleren, skal tællerens værdi være 1 større end den tidligere værdi

Hvis vi glemmer at gennemføre et hvilket som helst trin i scenariet, fortæller rapporten os. Sig, at vi ikke implementerede @Hvornår trin:

Scenarie: når en bruger øger en tæller, øges dens værdi med 1 givet en tæller Og tælleren har en hvilken som helst integreret værdi Når brugeren øger tælleren (VENTENDE) Så skal tællerens værdi være 1 større end den tidligere værdi (IKKE UDFØRT )
@Når ("brugeren øger tælleren") @ Afventer offentlig ugyldighed, nårTheUserIncreasesTheCounter () {// PENDING}

Rapporten siger, at @Hvornår et trin afventer, og på grund af det, @Derefter trin udføres ikke.

Hvad hvis vores @Then-trin mislykkes? Vi kan se fejlen med det samme fra rapporten:

Scenarie: Når en bruger øger en tæller, øges dens værdi med 1 givet en tæller Og tælleren har en integreret værdi Når brugeren øger tælleren, skal tællerens værdi være 1 større end den tidligere værdi (FAILED) (java. lang.AssertionError)

5. Test af REST API

Nu har vi forstået det grundlæggende i JBhave; vi får se, hvordan man tester en REST API med det. Vores tests vil være baseret på vores tidligere artikel, der diskuterer, hvordan man tester REST API med Java.

I den artikel testede vi GitHub REST API og fokuserede hovedsageligt på HTTP-svarkode, headere og nyttelast. For enkelheds skyld kan vi skrive dem i henholdsvis tre separate historier.

5.1. Test af statuskoden

Historien:

Scenarie: når en bruger kontrollerer en ikke-eksisterende bruger på github, ville github svare 'ikke fundet' givet github-brugerprofil api og et tilfældigt ikke-eksisterende brugernavn Når jeg leder efter den tilfældige bruger via api Så svarer github: 404 ikke fundet Når jeg ser efter eugenp1 via api Så svarer github: 404 ikke fundet Når jeg ser efter eugenp2 via api Så svarer github: 404 ikke fundet

Trinene:

offentlig klasse GithubUserNotFoundSteps {private String api; privat streng nonExistentUser; privat int githubResponseCode; @Given ("github brugerprofil api") offentlig ugyldighed givenGithubUserProfileApi () {api = "//api.github.com/users/%s"; } @Given ("et tilfældigt ikke-eksisterende brugernavn") offentligt ugyldigt givetANonexistentUsername () {nonExistentUser = randomAlphabetic (8); } @When ("Jeg leder efter den tilfældige bruger via api") offentlig ugyldighed nårILookForTheUserViaTheApi () kaster IOException {githubResponseCode = getGithubUserProfile (api, nonExistentUser) .getStatusLine () .getStatusCode (); } @When ("Jeg leder efter $ bruger via api") offentlig ugyldighed, nårILookForSomeNonExistentUserViaTheApi (strengbruger) kaster IOException {githubResponseCode = getGithubUserProfile (api, bruger) .getStatusLine () .getStatusCode (); } @Then ("github svarer: 404 ikke fundet") offentlig ugyldig thenGithubRespond404NotFound () {assertTrue (SC_NOT_FOUND == githubResponseCode); } // ...}

Bemærk hvordan, i trinimplementeringen, vi brugte parameterindsprøjtningsfunktionen. Argumenterne ekstraheret fra trinkandidaten matches lige efter naturlig rækkefølge med parametrene i den annoterede Java-metode.

Annoterede navngivne parametre understøttes også:

@When ("Jeg ser efter $ brugernavn via api") offentlig ugyldighed nårILookForSomeNonExistentUserViaTheApi (@Named ("brugernavn") Stringbruger) kaster IOException

5.2. Test af medietypen

Her er en simpel MIME-type testhistorie:

Scenarie: når en bruger kontrollerer en gyldig brugerprofil på github, ville github svare json data Givet github brugerprofil api Og et gyldigt brugernavn Når jeg leder efter brugeren via api Så svarer github data af typen json

Og her er trinene:

offentlig klasse GithubUserResponseMediaTypeSteps {private String api; privat streng validUser; private String mediaType; @Given ("github brugerprofil api") offentlig ugyldighed givenGithubUserProfileApi () {api = "//api.github.com/users/%s"; } @Given ("et gyldigt brugernavn") offentligt ugyldigt givetAValidUsername () {validUser = "eugenp"; } @When ("Jeg leder efter brugeren via api") offentlig ugyldighed nårILookForTheUserViaTheApi () kaster IOException {mediaType = ContentType .getOrDefault (getGithubUserProfile (api, validUser) .getEntity ()) .getMimeType (); } @Then ("github svarer data af typen json") offentlig ugyldig derefterGithubRespondDataOfTypeJson () {assertEquals ("application / json", mediaType); }}

5.3. Test af JSON-nyttelasten

Derefter den sidste historie:

Scenarie: når en bruger kontrollerer en gyldig brugers profil på github, skal githubs svar json indeholde en login-nyttelast med det samme brugernavn Givet github-brugerprofil api Når jeg ser efter eugenp via api Så indeholder githubs svar en 'login' nyttelast, der er den samme som eugenp

Og den almindelige lige trin-implementering:

offentlig klasse GithubUserResponsePayloadSteps {private String api; privat GitHubUser-ressource; @Given ("github brugerprofil api") offentlig ugyldighed givenGithubUserProfileApi () {api = "//api.github.com/users/%s"; } @When ("Jeg ser efter $ bruger via api") offentlig ugyldighed nårILookForEugenpViaTheApi (strengbruger) kaster IOException {HttpResponse httpResponse = getGithubUserProfile (api, bruger); ressource = RetrieveUtil.retrieveResourceFromResponse (httpResponse, GitHubUser.class); } @Then ("githubs svar indeholder en 'login' nyttelast samme som $ brugernavn") offentlig ugyldig derefterGithubsResponseContainsAloginPayloadSameAsEugenp (streng brugernavn) {assertThat (brugernavn, Matchers.is (resource.getLogin ())); }}

6. Resume

I denne artikel har vi kort introduceret JBehave og implementeret REST API-test i BDD-stil.

Sammenlignet med vores almindelige Java-testkode ser kode implementeret med JBehave meget klar og intuitiv ud, og testresultatrapporten ser meget mere elegant ud.

Som altid kan eksempelkoden findes i Github-projektet.


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