Testen einer REST-API mit JBehave

1. Einleitung

In diesem Artikel werfen wir einen kurzen Blick auf JBehave und konzentrieren uns dann auf das Testen einer REST-API aus einer BDD-Perspektive.

2. JBehave und BDD

JBehave ist ein verhaltensgesteuertes Entwicklungsframework. Es soll eine intuitive und zugängliche Möglichkeit für automatisierte Abnahmetests bieten.

Wenn Sie mit BDD nicht vertraut sind, sollten Sie mit diesem Artikel beginnen, der sich mit einem anderen BDD-Testframework befasst - Cucumber, in dem wir die allgemeine BDD-Struktur und -Funktionen vorstellen.

Ähnlich wie bei anderen BDD-Frameworks übernimmt JBehave die folgenden Konzepte:

  • Story - stellt ein automatisch ausführbares Inkrement der Geschäftsfunktionalität dar und umfasst ein oder mehrere Szenarien
  • Szenarien - stellen konkrete Beispiele für das Verhalten des Systems dar
  • Schritte - Stellen Sie das tatsächliche Verhalten mithilfe klassischer BDD-Schlüsselwörter dar: Gegeben , Wann und Dann

Ein typisches Szenario wäre:

Given a precondition When an event occurs Then the outcome should be captured

Jeder Schritt im Szenario entspricht einer Anmerkung in JBehave:

  • @Given : Initiieren Sie den Kontext
  • @Wenn : mach die Aktion
  • @ Dann : Testen Sie das erwartete Ergebnis

3. Maven-Abhängigkeit

Um die Verwendung von JBehave in unserem Maven - Projekt, die JBehave-Core - Abhängigkeit sollte im vornehmen enthalten sein pom :

 org.jbehave jbehave-core 4.1 test 

4. Ein kurzes Beispiel

Um JBehave verwenden zu können, müssen Sie die folgenden Schritte ausführen:

  1. Schreiben Sie eine User Story
  2. Ordnen Sie Schritte von der User Story dem Java-Code zu
  3. Konfigurieren Sie User Stories
  4. Führen Sie JBehave-Tests aus
  5. Ergebnisse überprüfen

4.1. Geschichte

Beginnen wir mit der folgenden einfachen Geschichte: „Als Benutzer möchte ich einen Zähler erhöhen, damit sich der Wert des Zählers um 1 erhöht.“

Wir können die Geschichte in einer .story- Datei definieren:

Scenario: when a user increases a counter, its value is increased by 1 Given a counter And the counter has any integral value When the user increases the counter Then the value of the counter must be 1 greater than previous value

4.2. Mapping-Schritte

Lassen Sie uns dies angesichts der Schritte in Java implementieren:

public class 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 = counter; } @When("the user increases the counter") public void increasesTheCounter() { counter++; } @Then("the value of the counter must be 1 greater than previous value") public void theValueOfTheCounterMustBe1Greater() { assertTrue(1 == counter - previousValue); } }

Denken Sie daran, dass der Wert in den Anmerkungen genau mit der Beschreibung übereinstimmen muss .

4.3. Unsere Geschichte konfigurieren

Um die Schritte auszuführen, müssen wir die Bühne für unsere Geschichte einrichten:

public class IncreaseStoryLiveTest extends JUnitStories { @Override public Configuration configuration() { return new MostUsefulConfiguration() .useStoryLoader(new LoadFromClasspath(this.getClass())) .useStoryReporterBuilder(new StoryReporterBuilder() .withCodeLocation(codeLocationFromClass(this.getClass())) .withFormats(CONSOLE)); } @Override public InjectableStepsFactory stepsFactory() { return new InstanceStepsFactory(configuration(), new IncreaseSteps()); } @Override protected List storyPaths() { return Arrays.asList("increase.story"); } }

In storyPaths () stellen wir unseren .story- Dateipfad bereit , der von JBehave analysiert werden soll. Die Implementierung der tatsächlichen Schritte finden Sie in stepFactory () . Dann werden in configuration () der Story Loader und der Story Report ordnungsgemäß konfiguriert.

Jetzt, da wir alles fertig haben, können wir unsere Geschichte einfach mit: mvn clean test beginnen .

4.4. Testergebnisse überprüfen

Wir können unser Testergebnis in der Konsole sehen. Da unsere Tests erfolgreich bestanden wurden, würde die Ausgabe mit unserer Geschichte dieselbe sein:

Scenario: when a user increases a counter, its value is increased by 1 Given a counter And the counter has any integral value When the user increases the counter Then the value of the counter must be 1 greater than previous value

Wenn wir vergessen, einen Schritt des Szenarios zu implementieren, werden wir im Bericht darüber informiert. Angenommen, wir haben den Schritt @When nicht implementiert :

Scenario: when a user increases a counter, its value is increased by 1 Given a counter And the counter has any integral value When the user increases the counter (PENDING) Then the value of the counter must be 1 greater than previous value (NOT PERFORMED)
@When("the user increases the counter") @Pending public void whenTheUserIncreasesTheCounter() { // PENDING }

Der Bericht würde sagen, wenn ein Schritt ansteht, und aus diesem Grund würde der Schritt @ dann nicht ausgeführt.

Was ist, wenn unser @ Dann-Schritt fehlschlägt? Wir können den Fehler sofort aus dem Bericht erkennen:

Scenario: when a user increases a counter, its value is increased by 1 Given a counter And the counter has any integral value When the user increases the counter Then the value of the counter must be 1 greater than previous value (FAILED) (java.lang.AssertionError)

5. Testen der REST-API

Jetzt haben wir die Grundlagen von JBhave verstanden . Wir werden sehen, wie man eine REST-API damit testet. Unsere Tests basieren auf unserem vorherigen Artikel zum Testen der REST-API mit Java.

In diesem Artikel haben wir die GitHub-REST-API getestet und uns hauptsächlich auf den HTTP-Antwortcode, die Header und die Nutzdaten konzentriert. Der Einfachheit halber können wir sie jeweils in drei separate Geschichten schreiben.

5.1. Testen des Statuscodes

Die Geschichte:

Scenario: when a user checks a non-existent user on github, github would respond 'not found' Given github user profile api And a random non-existent username When I look for the random user via the api Then github respond: 404 not found When I look for eugenp1 via the api Then github respond: 404 not found When I look for eugenp2 via the api Then github respond: 404 not found

Die Schritte:

public class GithubUserNotFoundSteps { private String api; private String nonExistentUser; private int githubResponseCode; @Given("github user profile api") public void givenGithubUserProfileApi() { api = "//api.github.com/users/%s"; } @Given("a random non-existent username") public void givenANonexistentUsername() { nonExistentUser = randomAlphabetic(8); } @When("I look for the random user via the api") public void whenILookForTheUserViaTheApi() throws IOException { githubResponseCode = getGithubUserProfile(api, nonExistentUser) .getStatusLine() .getStatusCode(); } @When("I look for $user via the api") public void whenILookForSomeNonExistentUserViaTheApi( String user) throws IOException { githubResponseCode = getGithubUserProfile(api, user) .getStatusLine() .getStatusCode(); } @Then("github respond: 404 not found") public void thenGithubRespond404NotFound() { assertTrue(SC_NOT_FOUND == githubResponseCode); } //... }

Beachten Sie, wie wir in den Schritten der Implementierung die Parameterinjektionsfunktion verwendet haben . Die aus dem Schrittkandidaten extrahierten Argumente werden nur in natürlicher Reihenfolge mit den Parametern in der mit Anmerkungen versehenen Java-Methode abgeglichen.

Außerdem werden mit Anmerkungen versehene benannte Parameter unterstützt:

@When("I look for $username via the api") public void whenILookForSomeNonExistentUserViaTheApi( @Named("username") String user) throws IOException

5.2. Testen des Medientyps

Hier ist eine einfache Geschichte zum Testen von MIME-Typen:

Scenario: when a user checks a valid user's profile on github, github would respond json data Given github user profile api And a valid username When I look for the user via the api Then github respond data of type json

Und hier sind die Schritte:

public class GithubUserResponseMediaTypeSteps { private String api; private String validUser; private String mediaType; @Given("github user profile api") public void givenGithubUserProfileApi() { api = "//api.github.com/users/%s"; } @Given("a valid username") public void givenAValidUsername() { validUser = "eugenp"; } @When("I look for the user via the api") public void whenILookForTheUserViaTheApi() throws 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. Testen der JSON-Nutzlast

Dann die letzte Geschichte:

Scenario: when a user checks a valid user's profile on github, github's response json should include a login payload with the same username Given github user profile api When I look for eugenp via the api Then github's response contains a 'login' payload same as eugenp

Und die einfache Implementierung in geraden Schritten:

public class GithubUserResponsePayloadSteps { private String api; private GitHubUser resource; @Given("github user profile api") public void givenGithubUserProfileApi() { api = "//api.github.com/users/%s"; } @When("I look for $user via the api") public void whenILookForEugenpViaTheApi(String user) throws IOException { HttpResponse httpResponse = getGithubUserProfile(api, user); resource = RetrieveUtil.retrieveResourceFromResponse(httpResponse, GitHubUser.class); } @Then("github's response contains a 'login' payload same as $username") public void thenGithubsResponseContainsAloginPayloadSameAsEugenp(String username) { assertThat(username, Matchers.is(resource.getLogin())); } }

6. Zusammenfassung

In diesem Artikel haben wir JBehave kurz vorgestellt und REST-API-Tests im BDD-Stil implementiert.

Im Vergleich zu unserem einfachen Java-Testcode sieht der mit JBehave implementierte Code viel klarer und intuitiver aus und der Testergebnisbericht sieht viel eleganter aus.

Der Beispielcode befindet sich wie immer im Github-Projekt.