Testen einer REST-API mit JBehave

Testen einer REST-API mit JBehave

1. Einführung

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

2. JBehave und BDD

JBehave ist ein behaviour Driven Development Framework. Es soll eine intuitive und zugängliche Möglichkeit für automatisierte Abnahmetests bieten.

Wenn Sie mit BDD nicht vertraut sind, sollten Sie mitthis article, covering on another BDD testing framework – Cucumber beginnen, in dem wir die allgemeine BDD-Struktur und -Funktionen vorstellen.

Ähnlich wie bei anderen BDD-Frameworks verwendet 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:Given,When undThen

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: Initiiert den Kontext

  • @When: Führen Sie die Aktion aus

  • @Then: Testen Sie das erwartete Ergebnis

3. Maven-Abhängigkeit

Um JBehave in unserem Maven-Projekt verwenden zu können, sollte die Abhängigkeit vonjbehave-coreinpom enthalten sein:


    org.jbehave
    jbehave-core
    4.1
    test

4. Ein kurzes Beispiel

Um JBehave zu verwenden, müssen wir die folgenden Schritte ausführen:

  1. Schreiben Sie eine User Story

  2. Ordnen Sie Schritte aus der User Story dem Java-Code zu

  3. User Stories konfigurieren

  4. Führen Sie JBehave-Tests aus

  5. Überprüfen Sie die Ergebnisse

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. Zuordnungsschritte

Lassen Sie uns dies anhand 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, dassthe value in the annotations must accurately match the description.

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");
    }

}

InstoryPaths() geben wir den Dateipfad von.storyan, der von JBehave analysiert werden soll. Die Implementierung der tatsächlichen Schritte wird instepsFactory() angegeben. Inconfiguration() sind der Story Loader und der Story Report ordnungsgemäß konfiguriert.

Nachdem wir alles fertig haben, können wir unsere Geschichte einfach mitmvn 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 identisch 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 umzusetzen, werden wir im Bericht darüber informiert. Angenommen, wir haben den Schritt@Whennicht 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, dass der Schritt@When aussteht, und aus diesem Grund würde der Schritt@Then nicht ausgeführt.

Was passiert, wenn unser @Then-Schritt fehlschlägt? Wir können den Fehler sofort aus dem Bericht ersehen:

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 vonJBhave verstanden; Wir werden sehen, wie Sie eine REST-API damit testen. Unsere Tests basieren auf unserenprevious article discussing how to test REST API with Java.

In diesem Artikel haben wir dieGitHub 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 verschiedene 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 = "https://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 in den Schritten Implementierungwe used the parameter injection feature. 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 = "https://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 = "https://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 inthe Github project.