Testing av en REST API med JBehave

1. Introduksjon

I denne artikkelen vil vi se raskt på JBehave, og deretter fokusere på å teste en REST API fra et BDD-perspektiv.

2. JBehave og BDD

JBehave er et Behavior Driven Development framework. Den har til hensikt å gi en intuitiv og tilgjengelig måte for automatisert akseptantesting.

Hvis du ikke er kjent med BDD, er det en god ide å begynne med denne artikkelen, som dekker et annet BDD-testrammeverk - Agurk, der vi introduserer den generelle BDD-strukturen og funksjonene.

I likhet med andre BDD-rammer vedtar JBehave følgende konsepter:

  • Story - representerer en automatisk kjørbar økning av forretningsfunksjonalitet, inneholder ett eller flere scenarier
  • Scenarier - representerer konkrete eksempler på systemets oppførsel
  • Trinn - representerer faktisk atferd ved bruk av klassiske BDD-nøkkelord: Gitt, Når og Deretter

Et typisk scenario vil være:

Gitt en forutsetning Når en hendelse inntreffer, bør resultatet fanges

Hvert trinn i scenariet tilsvarer en kommentar i JBehave:

  • @Given: initier konteksten
  • @Når: gjør handlingen
  • @Deretter: test forventet utfall

3. Maven avhengighet

For å gjøre bruk av JBehave i vårt maven-prosjekt, bør avhengigheten av jbehave-kjerne inkluderes i pom:

 org.jbehave jbehave-core 4.1 test 

4. Et raskt eksempel

For å bruke JBehave, må vi følge følgende trinn:

  1. Skriv en brukerhistorie
  2. Kartlegg trinn fra brukerhistorien til Java-kode
  3. Konfigurer brukerhistorier
  4. Kjør JBehave-tester
  5. Gjennomgå resultatene

4.1. Historie

La oss starte med følgende enkle historie: “som bruker vil jeg øke en teller, slik at jeg kan få tellerens verdi øke med 1”.

Vi kan definere historien i en .historie fil:

Scenario: når en bruker øker en teller, økes verdien med 1 Gitt en teller Og telleren har en integralverdi Når brukeren øker telleren, må verdien på telleren være 1 større enn forrige verdi

4.2. Kartleggingstrinn

Gitt trinnene, la oss implementere dette i Java:

offentlig klasse IncreaseSteps {private int counter; private int previousValue; @Given ("a counter") public void aCounter () {} @Given ("the counter has any integral value") public void counterHasAnyIntegralValue () {counter = new Random (). NextInt (); previousValue = teller; } @Når ("brukeren øker telleren") offentlig tomrom økerTheCounter () {teller ++; } @Then ("verdien på telleren må være 1 større enn forrige verdi") offentlig annullereValueOfTheCounterMustBe1Greater () {assertTrue (1 == teller - forrigeValue); }}

Husk at verdien i kommentarene må stemme overens med beskrivelsen.

4.3. Konfigurere historien vår

For å utføre trinnene, må vi sette opp scenen for historien vår:

offentlig klasse IncreaseStoryLiveTest utvider JUnitStories {@Override public Configuration configuration () {return new MostUsefulConfiguration () .useStoryLoader (new LoadFromClasspath (this.getClass ())) .useStoryReporterBuilder (new LoadFromClasspath (this.getClass ())) .useStoryReporterBuilder (new StoryReporterBuilder () .withLocationClassLocation ) .withFormats (CONSOLE)); } @ Override public InjectableStepsFactory stepsFactory () {returner ny InstanceStepsFactory (konfigurasjon (), ny IncreaseSteps ()); } @ Override-beskyttet Liste storyPaths () {return Arrays.asList ("increase.story"); }}

I storyPaths (), vi gir vår .historie filsti som skal analyseres av JBehave. Faktiske trinn implementering er gitt i trinnFabrikk (). Så inn konfigurasjon (), historielaster og historierapport er riktig konfigurert.

Nå som vi har alt klart, kan vi begynne historien vår ved å løpe: mvn ren test.

4.4. Gjennomgang av testresultater

Vi kan se testresultatet vårt i konsollen. Ettersom testene våre har bestått, vil produksjonen være den samme med historien vår:

Scenario: når en bruker øker en teller, økes verdien med 1 Gitt en teller Og telleren har en integralverdi Når brukeren øker telleren, må verdien på telleren være 1 større enn forrige verdi

Hvis vi glemmer å implementere noen trinn i scenariet, vil rapporten gi oss beskjed. Si at vi ikke implementerte @Når steg:

Scenario: når en bruker øker en teller, økes verdien med 1 Gitt en teller Og telleren har en hvilken som helst integrert verdi Når brukeren øker telleren (VENTENDE) Da må tellerens verdi være 1 større enn forrige verdi (IKKE UTFØRT )
@Når ("brukeren øker telleren") @Venter offentlig ugyldig nårTheUserIncreasesTheCounter () {// PENDING}

Rapporten vil si at @Når et skritt venter, og på grunn av det, @Deretter trinn ville ikke utføres.

Hva om @Then-trinnet vårt mislykkes? Vi kan oppdage feilen med en gang fra rapporten:

Scenario: når en bruker øker en teller, økes verdien med 1 Gitt en teller Og telleren har en integralverdi Når brukeren øker telleren, må verdien på telleren være 1 større enn forrige verdi (FAILED) (java. lang.AssertionError)

5. Testing av REST API

Nå har vi forstått det grunnleggende om JBhave; vi får se hvordan vi kan teste et REST API med det. Våre tester vil være basert på vår forrige artikkel som diskuterer hvordan du tester REST API med Java.

I den artikkelen testet vi GitHub REST API og fokuserte hovedsakelig på HTTP-responskode, overskrifter og nyttelast. For enkelhets skyld kan vi skrive dem i henholdsvis tre separate historier.

5.1. Testing av statuskoden

Historien:

Scenario: når en bruker sjekker en ikke-eksisterende bruker på github, ville github svare 'ikke funnet' Gitt github brukerprofil api Og et tilfeldig ikke-eksisterende brukernavn Når jeg ser etter den tilfeldige brukeren via api Da svarer github: 404 ikke funnet Når jeg ser etter eugenp1 via api Da svarer github: 404 ikke når jeg ser etter eugenp2 via api Da svarer github: 404 ikke

Stegene:

offentlig klasse GithubUserNotFoundSteps {private String api; privat streng nonExistentUser; privat int githubResponseCode; @Given ("github brukerprofil api") offentlig ugyldig givenGithubUserProfileApi () {api = "//api.github.com/users/%s"; } @Given ("et tilfeldig ikke-eksisterende brukernavn") offentlig ugyldig gittANonexistentUsername () {nonExistentUser = randomAlphabetic (8); } @When ("Jeg ser etter den tilfeldige brukeren via api") offentlig ugyldig nårILookForTheUserViaTheApi () kaster IOException {githubResponseCode = getGithubUserProfile (api, nonExistentUser) .getStatusLine () .getStatusCode (); } @When ("Jeg ser etter $ bruker via api") offentlig ugyldig nårILookForSomeNonExistentUserViaTheApi (strengbruker) kaster IOException {githubResponseCode = getGithubUserProfile (api, bruker) .getStatusLine () .getStatusCode (); } @Then ("github svarer: 404 ikke funnet") offentlig ugyldig thenGithubRespond404NotFound () {assertTrue (SC_NOT_FOUND == githubResponseCode); } // ...}

Legg merke til hvordan, i trinn implementering, vi brukte parameterinnsprøytingsfunksjonen. Argumentene hentet fra trinnkandidaten samsvares bare i naturlig rekkefølge med parametrene i den merkede Java-metoden.

Kommenterte navngitte parametere støttes også:

@When ("Jeg ser etter $ brukernavn via api") offentlig ugyldig nårILookForSomeNonExistentUserViaTheApi (@Named ("brukernavn") strengbruker) kaster IOException

5.2. Testing av medietypen

Her er en enkel MIME-type testhistorie:

Scenario: når en bruker sjekker en gyldig brukerprofil på github, ville github svare json data Gitt github brukerprofil api Og et gyldig brukernavn Når jeg ser etter brukeren via api Så svarer github data av typen json

Og her er trinnene:

offentlig klasse GithubUserResponseMediaTypeSteps {private String api; privat streng validUser; private String mediaType; @Given ("github brukerprofil api") offentlig ugyldig givenGithubUserProfileApi () {api = "//api.github.com/users/%s"; } @Given ("et gyldig brukernavn") offentlig ugyldig gittAValidUsername () {validUser = "eugenp"; } @When ("Jeg ser etter brukeren via api") offentlig ugyldig nårILookForTheUserViaTheApi () kaster IOException {mediaType = ContentType .getOrDefault (getGithubUserProfile (api, validUser) .getEntity ()) .getMimeType (); } @Then ("github respond data of type json") public void thenGithubRespondDataOfTypeJson () {assertEquals ("application / json", mediaType); }}

5.3. Testing av JSON nyttelast

Så den siste historien:

Scenario: når en bruker sjekker en gyldig brukerprofil på github, skal githubs svar json inneholde en påloggingsbelastning med samme brukernavn Gitt github brukerprofil api Når jeg ser etter eugenp via api Da inneholder githubs svar en 'login' nyttelast som eugenp

Og den enkle trinnvise implementeringen:

offentlig klasse GithubUserResponsePayloadSteps {private String api; privat GitHubUser-ressurs; @Given ("github brukerprofil api") offentlig ugyldig givenGithubUserProfileApi () {api = "//api.github.com/users/%s"; } @When ("Jeg ser etter $ bruker via api") offentlig ugyldig nårILookForEugenpViaTheApi (strengbruker) kaster IOException {HttpResponse httpResponse = getGithubUserProfile (api, bruker); ressurs = RetrieveUtil.retrieveResourceFromResponse (httpResponse, GitHubUser.class); } @Then ("githubs svar inneholder en" login "nyttelast som $ brukernavn") offentlig ugyldig thenGithubsResponseContainsAloginPayloadSameAsEugenp (streng brukernavn) {assertThat (brukernavn, Matchers.is (resource.getLogin ())); }}

6. Sammendrag

I denne artikkelen har vi kort introdusert JBehave og implementert REST API-tester i BDD-stil.

Sammenlignet med vår vanlige Java-testkode, ser kode implementert med JBehave mye tydelig og intuitiv ut, og testresultatrapporten ser mye mer elegant ut.

Som alltid kan eksempelkoden bli funnet i Github-prosjektet.