Test d’une API REST avec JBehave

Test d'une API REST avec JBehave

Présentation de1.

Dans cet article, nous examinerons rapidementJBehave, puis nous nous concentrerons sur le test d'une API REST du point de vue BDD.

2. JBehave et BDD

JBehave est un framework de développement basé sur le comportement. Il vise à fournir un moyen intuitif et accessible pour les tests d'acceptation automatisés.

Si vous n'êtes pas familier avec BDD, c'est une bonne idée de commencer parthis article, covering on another BDD testing framework – Cucumber, dans lequel nous présentons la structure et les fonctionnalités générales de BDD.

Semblable à d'autres frameworks BDD, JBehave adopte les concepts suivants:

  • Story - représente un incrément d'exécution automatique des fonctionnalités de l'entreprise, comprend un ou plusieurs scénarios

  • Scénarios - représentent des exemples concrets du comportement du système

  • Étapes - représentent le comportement réel à l'aide de mots clés BDD classiques:Given,When etThen

Un scénario typique serait:

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

Chaque étape du scénario correspond à une annotation dans JBehave:

  • @Given: lancer le contexte

  • @When: effectuez l'action

  • @Then: tester le résultat attendu

3. Dépendance Maven

Pour utiliser JBehave dans notre projet maven, la dépendancejbehave-core doit être incluse dans lespom:


    org.jbehave
    jbehave-core
    4.1
    test

4. Un exemple rapide

Pour utiliser JBehave, nous devons suivre les étapes suivantes:

  1. Ecrire une user story

  2. Mappez les étapes de la user story au code Java

  3. Configurer les user stories

  4. Exécuter des tests JBehave

  5. Revoir les résultats

4.1. Récit

Commençons par l'histoire simple suivante: "en tant qu'utilisateur, je souhaite augmenter un compteur, afin que je puisse faire augmenter la valeur du compteur de 1".

Nous pouvons définir l'histoire dans un fichier.story:

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. Étapes de la cartographie

Compte tenu des étapes, implémentons ceci en Java:

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

N'oubliez pas quethe value in the annotations must accurately match the description.

4.3. Configurer notre histoire

Pour effectuer les étapes, nous devons configurer la scène de notre histoire:

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

}

DansstoryPaths(), nous fournissons notre chemin de fichier.story à analyser par JBehave. L'implémentation des étapes réelles est fournie enstepsFactory(). Ensuite, dansconfiguration(), le chargeur d'histoire et le rapport d'histoire sont correctement configurés.

Maintenant que tout est prêt, nous pouvons commencer notre histoire simplement en exécutant:mvn clean test.

4.4. Consulter les résultats des tests

Nous pouvons voir notre résultat de test dans la console. Comme nos tests ont réussi, le résultat serait le même avec notre récit:

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

Si nous oublions de mettre en œuvre une étape du scénario, le rapport nous le fera savoir. Supposons que nous n’avons pas mis en œuvre l’étape@When:

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
}

Le rapport indiquerait que le@When une étape est en attente, et à cause de cela, l'étape@Then ne serait pas exécutée.

Et si notre étape @Then échoue? Nous pouvons repérer l’erreur tout de suite dans le rapport:

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. Test de l'API REST

Nous avons maintenant compris les bases deJBhave; nous allons voir comment tester une API REST avec. Nos tests seront basés sur nosprevious article discussing how to test REST API with Java.

Dans cet article, nous avons testé lesGitHub REST API et nous nous sommes principalement concentrés sur le code de réponse HTTP, les en-têtes et la charge utile. Pour simplifier, nous pouvons les écrire en trois histoires distinctes.

5.1. Test du code d'état

L'histoire:

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

Les marches:

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

    //...
}

Remarquez comment, dans l'implémentation des étapes,we used the parameter injection feature. Les arguments extraits de l'étape candidate sont simplement mis en correspondance selon l'ordre naturel avec les paramètres de la méthode Java annotée.

De plus, les paramètres nommés annotés sont supportés:

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

5.2. Test du type de support

Voici une histoire simple de test de type MIME:

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

Et voici les étapes:

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. Test de la charge utile JSON

Puis la dernière histoire:

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

Et la mise en œuvre des étapes droites simples:

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

Dans cet article, nous avons brièvement présenté JBehave et mis en œuvre des tests d'API REST de style BDD.

Comparé à notre code de test Java, le code implémenté avec JBehave semble beaucoup plus clair et intuitif et le rapport de résultat du test est beaucoup plus élégant.

Comme toujours, l'exemple de code se trouve dansthe Github project.