Een REST API testen met JBehave

1. Invoering

In dit artikel zullen we JBehave kort bekijken en ons vervolgens concentreren op het testen van een REST API vanuit een BDD-perspectief.

2. JBehave en BDD

JBehave is een raamwerk voor gedragsgestuurde ontwikkeling. Het is bedoeld om een ​​intuïtieve en toegankelijke manier te bieden voor geautomatiseerde acceptatietests.

Als u niet bekend bent met BDD, is het een goed idee om met dit artikel te beginnen en een ander BDD-testraamwerk te behandelen - Komkommer, waarin we de algemene BDD-structuur en -functies introduceren.

Net als bij andere BDD-frameworks, past JBehave de volgende concepten toe:

  • Verhaal - vertegenwoordigt een automatisch uitvoerbare toename van bedrijfsfunctionaliteit, omvat een of meer scenario's
  • Scenario's - vertegenwoordigen concrete voorbeelden van het gedrag van het systeem
  • Stappen - geef het werkelijke gedrag weer met behulp van klassieke BDD-trefwoorden: Gegeven, Wanneer en Dan

Een typisch scenario zou zijn:

Gegeven een randvoorwaarde Wanneer zich een gebeurtenis voordoet, moet de uitkomst worden vastgelegd

Elke stap in het scenario komt overeen met een annotatie in JBehave:

  • @Gegeven: start de context
  • @Wanneer: doe de actie
  • @Dan: test het verwachte resultaat

3. Maven Afhankelijkheid

Om gebruik te maken van JBehave in ons maven-project, moet de jbehave-core-afhankelijkheid worden opgenomen in de pom:

 org.jbehave jbehave-core 4.1 test 

4. Een snel voorbeeld

Om JBehave te gebruiken, moeten we de volgende stappen volgen:

  1. Schrijf een gebruikersverhaal
  2. Breng stappen van het gebruikersverhaal naar Java-code in kaart
  3. Configureer gebruikersverhalen
  4. Voer JBehave-tests uit
  5. Bekijk de resultaten

4.1. Verhaal

Laten we beginnen met het volgende eenvoudige verhaal: "als gebruiker wil ik een teller verhogen, zodat ik de waarde van de teller met 1 kan laten verhogen".

We kunnen het verhaal definiëren in een .verhaal het dossier:

Scenario: wanneer een gebruiker een teller verhoogt, wordt de waarde ervan verhoogd met 1 Gegeven een teller En de teller heeft een integrale waarde Wanneer de gebruiker de teller verhoogt Dan moet de waarde van de teller 1 groter zijn dan de vorige waarde

4.2. Stappen in kaart brengen

Laten we gezien de stappen dit in Java implementeren:

openbare klasse IncreaseSteps {privé int teller; private int previousValue; @Given ("een teller") public void aCounter () {} @Given ("de teller heeft een integrale waarde") public void counterHasAnyIntegralValue () {counter = new Random (). NextInt (); previousValue = teller; } @When ("de gebruiker verhoogt de teller") public void verhoogtTheCounter () {counter ++; } @Then ("de waarde van de teller moet 1 groter zijn dan de vorige waarde") public void theValueOfTheCounterMustBe1Greater () {assertTrue (1 == counter - previousValue); }}

Onthoud dat de waarde in de annotaties moet nauwkeurig overeenkomen met de beschrijving.

4.3. Ons verhaal configureren

Om de stappen uit te voeren, moeten we het podium voor ons verhaal opzetten:

openbare klasse IncreaseStoryLiveTest breidt JUnitStories uit {@Override openbare configuratieconfiguratie () {retourneer nieuwe MostUsefulConfiguration () .useStoryLoader (nieuwe LoadFromClasspath (this.getClass ())) .useStoryReporterBuilder (nieuwe StoryReporterBuilder (). ) .withFormats (CONSOLE)); } @Override openbare InjectableStepsFactory stepsFactory () {retourneer nieuwe InstanceStepsFactory (configuratie (), nieuwe IncreaseSteps ()); } @Override beschermde lijst storyPaths () {return Arrays.asList ("increment.story"); }}

In storyPaths (), wij bieden onze .verhaal bestandspad dat door JBehave moet worden geparseerd. De daadwerkelijke implementatie van de stappen wordt gegeven in stepsFactory (). Dan in configuratie(), zijn de verhaallader en het verhaalrapport correct geconfigureerd.

Nu we alles klaar hebben, kunnen we ons verhaal beginnen door simpelweg uit te voeren: mvn schone test.

4.4. Testresultaten bekijken

We kunnen ons testresultaat zien in de console. Aangezien onze tests met succes zijn doorstaan, zou de output hetzelfde zijn met ons verhaal:

Scenario: wanneer een gebruiker een teller verhoogt, wordt de waarde ervan verhoogd met 1 Gegeven een teller En de teller heeft een integrale waarde Wanneer de gebruiker de teller verhoogt Dan moet de waarde van de teller 1 groter zijn dan de vorige waarde

Als we een stap van het scenario vergeten te implementeren, laat het rapport ons dit weten. Stel dat we het @Wanneer stap:

Scenario: wanneer een gebruiker een teller verhoogt, wordt zijn waarde verhoogd met 1 Gegeven een teller En de teller heeft een integrale waarde Wanneer de gebruiker de teller verhoogt (PENDING) Dan moet de waarde van de teller 1 groter zijn dan de vorige waarde (NIET UITGEVOERD )
@When ("de gebruiker verhoogt de teller") @Pending public void whenTheUserIncreasesTheCounter () {// PENDING}

Het rapport zou de @Wanneer een stap is in behandeling en daarom is de @Dan stap zou niet worden uitgevoerd.

Wat als onze @Then-stap mislukt? We kunnen de fout meteen in het rapport zien:

Scenario: wanneer een gebruiker een teller verhoogt, wordt de waarde ervan verhoogd met 1 Gegeven een teller En de teller heeft een integrale waarde Wanneer de gebruiker de teller verhoogt Dan moet de waarde van de teller 1 groter zijn dan de vorige waarde (FAILED) (java. lang.AssertionError)

5. REST API testen

Nu hebben we de basis van JBhave; we zullen zien hoe we er een REST API mee kunnen testen. Onze tests zijn gebaseerd op ons vorige artikel waarin wordt besproken hoe REST API met Java kan worden getest.

In dat artikel hebben we de GitHub REST API getest en hebben we ons voornamelijk gericht op de HTTP-responscode, headers en payload. Voor de eenvoud kunnen we ze respectievelijk in drie afzonderlijke verhalen schrijven.

5.1. De statuscode testen

Het verhaal:

Scenario: wanneer een gebruiker een niet-bestaande gebruiker op github controleert, antwoordt github 'niet gevonden' Gegeven github-gebruikersprofiel api En een willekeurige niet-bestaande gebruikersnaam Wanneer ik de willekeurige gebruiker zoek via de api Dan antwoord github: 404 niet gevonden Als ik eugenp1 zoek via de api dan antwoord github: 404 niet gevonden Als ik eugenp2 zoek via de api dan antwoord github: 404 niet gevonden

De stappen:

openbare klasse GithubUserNotFoundSteps {private String api; private String nonExistentUser; privé int githubResponseCode; @Given ("github gebruikersprofiel api") openbare leegte gegevenGithubUserProfileApi () {api = "//api.github.com/users/%s"; } @Given ("een willekeurige niet-bestaande gebruikersnaam") openbare leegte gegevenANonexistentUsername () {nonExistentUser = randomAlphabetic (8); } @When ("Ik zoek naar de willekeurige gebruiker via de api") public void whenILookForTheUserViaTheApi () gooit IOException {githubResponseCode = getGithubUserProfile (api, nonExistentUser) .getStatusLine () .getStatusCode (); } @When ("Ik zoek $ user via de api") public void whenILookForSomeNonExistentUserViaTheApi (String-gebruiker) gooit IOException {githubResponseCode = getGithubUserProfile (api, gebruiker) .getStatusLine () .getStatusCode (); } @Then ("github antwoord: 404 niet gevonden") public void thenGithubRespond404NotFound () {assertTrue (SC_NOT_FOUND == githubResponseCode); } // ...}

Merk op hoe, in de implementatie van de stappen, we gebruikten de parameterinjectiefunctie. De argumenten die uit de stapkandidaat zijn geëxtraheerd, worden zojuist in de natuurlijke volgorde vergeleken met de parameters in de geannoteerde Java-methode.

Ook worden geannoteerde benoemde parameters ondersteund:

@When ("Ik zoek naar $ gebruikersnaam via de api") public void whenILookForSomeNonExistentUserViaTheApi (@Named ("gebruikersnaam") String-gebruiker) gooit IOException

5.2. Het mediatype testen

Hier is een eenvoudig verhaal over het testen van het MIME-type:

Scenario: wanneer een gebruiker een geldig gebruikersprofiel op github controleert, zou github reageren op json-gegevens Gegeven github-gebruikersprofiel api En een geldige gebruikersnaam Wanneer ik de gebruiker zoek via de api Dan antwoordt github-gegevens van het type json

En hier zijn de stappen:

openbare klasse GithubUserResponseMediaTypeSteps {private String api; private String validUser; privé String mediatype; @Given ("github gebruikersprofiel api") openbare leegte gegevenGithubUserProfileApi () {api = "//api.github.com/users/%s"; } @Given ("een geldige gebruikersnaam") public void givenAValidUsername () {validUser = "eugenp"; } @When ("Ik zoek de gebruiker via de api") public void whenILookForTheUserViaTheApi () gooit IOException {mediaType = ContentType .getOrDefault (getGithubUserProfile (api, validUser) .getEntity ()) .getMimeType (); } @Then ("github antwoordgegevens van het type json") public void thenGithubRespondDataOfTypeJson () {assertEquals ("application / json", mediaType); }}

5.3. Testen van de JSON-payload

Dan het laatste verhaal:

Scenario: wanneer een gebruiker een geldig gebruikersprofiel op github controleert, moet het antwoord van github json een login-payload bevatten met dezelfde gebruikersnaam Gegeven github-gebruikersprofiel api Wanneer ik eugenp zoek via de api Dan bevat het antwoord van github een 'login'-payload hetzelfde als eugenp

En de eenvoudige implementatie van rechte stappen:

openbare klasse GithubUserResponsePayloadSteps {private String api; privé GitHubUser-bron; @Given ("github gebruikersprofiel api") openbare leegte gegevenGithubUserProfileApi () {api = "//api.github.com/users/%s"; } @When ("Ik zoek naar $ user via de api") public void whenILookForEugenpViaTheApi (String-gebruiker) gooit IOException {HttpResponse httpResponse = getGithubUserProfile (api, gebruiker); resource = RetrieveUtil.retrieveResourceFromResponse (httpResponse, GitHubUser.class); } @Then ("github's antwoord bevat een 'login' payload hetzelfde als $ gebruikersnaam") public void thenGithubsResponseContainsAloginPayloadSameAsEugenp (String gebruikersnaam) {assertThat (gebruikersnaam, Matchers.is (resource.getLogin ())); }}

6. Samenvatting

In dit artikel hebben we kort JBehave geïntroduceerd en REST API-tests in BDD-stijl geïmplementeerd.

In vergelijking met onze gewone Java-testcode, ziet code geïmplementeerd met JBehave er veel duidelijk en intuïtief uit en ziet het testresultaatrapport er veel eleganter uit.

Zoals altijd is de voorbeeldcode te vinden in het Github-project.