Test d'une API REST avec JBehave

1. Introduction

Dans cet article, nous examinerons rapidement JBehave, 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 piloté par 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 par cet article, couvrant un autre cadre de test BDD - 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:

  • Histoire - représente un incrément de fonctionnalité métier exécutable automatiquement, comprend un ou plusieurs scénarios
  • Scénarios - représentent des exemples concrets du comportement du système
  • Étapes - représenter le comportement réel à l'aide de mots-clés BDD classiques: donné , quand et alors

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 : initier le contexte
  • @Quand : faites l'action
  • @Then : testez le résultat attendu

3. Dépendance de Maven

Pour utiliser JBehave dans notre projet maven, la dépendance jbehave-core doit être incluse dans le pom :

 org.jbehave jbehave-core 4.1 test 

4. Un exemple rapide

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

  1. Rédiger une user story
  2. Mapper les étapes de la user story au code Java
  3. Configurer les user stories
  4. Exécuter les tests JBehave
  5. Examiner les résultats

4.1. Récit

Commençons par l'histoire simple suivante: «en tant qu'utilisateur, je veux 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 que la valeur des annotations doit correspondre exactement à la description .

4.3. Configurer notre histoire

Pour exécuter les étapes, nous devons préparer le terrain pour 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"); } }

Dans storyPaths () , nous fournissons notre chemin de fichier .story à analyser par JBehave. L'implémentation des étapes réelles est fournie dans stepsFactory () . Ensuite, dans configuration () , 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 histoire:

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. Disons que nous n'avons pas implémenté 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 @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 de JBhave ; nous verrons comment tester une API REST avec. Nos tests seront basés sur notre article précédent expliquant comment tester l'API REST avec Java.

Dans cet article, nous avons testé l'API REST GitHub 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 respectivement dans 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 = "//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, nous avons utilisé la fonction d'injection de paramètres . 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.

En outre, les paramètres nommés annotés sont pris en charge:

@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 de test de type MIME simple:

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 = "//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 simples:

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. Résumé

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

Par rapport à notre code de test Java simple, 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 dans le projet Github.