Essayer de rédiger les étapes de la fonctionnalité de concombre pour le test de l'API REST.
Je ne sais pas quelle approche est la meilleure:
Given I log in with username and password
When I add one "tv" into my cart
And I check my cart
Then I should see the item "tv" is in my cart
ou
Given the client authenticate with username and password
When the client send POST to "/cart/add" with body "{item: body}"
Then the response code should be "200"
And the response body should expect "{success: true}"
When the client send GET to "/cart"
Then the response code should be "200"
And the response body should expect "{"items": ["tv"]}"
Existe-t-il une convention à suivre lorsque des personnes essaient d'écrire des étapes de concombre pour l'API REST?
Je viens de tomber sur cet article utile: http://gregbee.ch/blog/effective-api-testing-with-cucumber
Résumer...
Scenario: List fruit
Given the system knows about the following fruit:
| name | color |
| banana | yellow |
| strawberry | red |
When the client requests a list of fruit
Then the response is a list containing 2 fruits
And one fruit has the following attributes:
| attribute | type | value |
| name | String | banana |
| color | String | yellow |
And one fruit has the following attributes:
| attribute | type | value |
| name | String | strawberry |
| color | String | red |
Valider un résultat par rapport à JSON est une tâche délicate, car si le résultat est un tableau, les éléments peuvent ne pas correspondre au même ordre que celui utilisé pour la validation dans le test.
Voici un exemple (assez proche) de ce que le programmeur pragmatique "The Cucumber Book" dit à propos du test des API REST via Cuke, et il semble se rapporter davantage à votre deuxième exemple:
Feature: Addresses
In order to complete the information on the place
I need an address
Scenario: Addresses
Given the system knows about the following addresses:
[INSERT TABLE HERE or GRAB FROM DATABASE]
When client requests GET /addresses
Then the response should be JSON:
"""
[
{"venue": "foo", "address": "bar"},
{ more stuff }
]
"""
STEP DEFINITION:
Given(/^the system knows about the following addresses:$/) do |addresses|
# table is a Cucumber::Ast::Table
File.open('addresses.json', 'w') do |io|
io.write(addresses.hashes.to_json)
end
end
When(/^client requests GET (.*)$/) do |path|
@last_response = HTTParty.get('local Host url goes here' + path)
end
Then /^the response should be JSON:$/ do |json|
JSON.parse(@last_response.body).should == JSON.parse(json)
end
ENV File:
require File.join(File.dirname(__FILE__), '..', '..', 'address_app')
require 'rack/test'
require 'json'
require 'sinatra'
require 'cucumber'
require 'httparty'
require 'childprocess'
require 'timeout'
server = ChildProcess.build("rackup", "--port", "9000")
server.start
Timeout.timeout(3) do
loop do
begin
HTTParty.get('local Host here')
break
rescue Errno::ECONNREFUSED => try_again
sleep 0.1
end
end
end
at_exit do
server.stop
end
J'ai utilisé concombre pour tester et surtout pour documenter l'API que j'ai créée à l'aide de Rails-api
dans mon projet actuel. J'ai cherché des outils à utiliser et j'ai fini par utiliser une combinaison de concombre-api-étapes et json_spec . Ça a bien marché pour moi.
Il n'y a pas de convention sur la façon d'écrire les étapes de concombre. La façon dont vous écrivez vos étapes dépend de la manière dont vous souhaitez utiliser votre suite de concombres. J'ai utilisé la sortie concombre comme référence pour nos devs clients JS Angular pour implémenter le client API. Ainsi, mes étapes relatives aux concombres contenaient les demandes et les réponses JSON réelles ainsi que le code d'état de chaque scénario. Cela rendait très facile la communication avec une équipe côté client chaque fois que quelque chose changeait (en particulier lorsque l'équipe côté client n'était pas physiquement présente sur mon lieu de travail).
Chaque fois que je créerais ou mettrais à jour une API, le serveur CI exécuterait un concombre dans le cadre de la construction et déplacerait la sortie au format HTML vers un emplacement "build_artifacts" pouvant être ouvert dans le navigateur. Les développeurs côté client obtiendraient toujours la référence la plus récente de cette façon.
J'ai écrit tout cela dans un article de blog sur la création d'une API JSON testée, documentée et versionnée , j'espère que cela vous aidera d'une manière ou d'une autre.
L'un des objectifs initiaux de Cucumber, qui contribue à sa conception, est de combler le fossé entre la mise en œuvre technique et les personnes connaissant les besoins de l'entreprise, afin que les descriptions de test puissent être écrites et/ou comprises par des non-développeurs. En tant que tel, il n’est pas parfaitement adapté aux spécifications techniques détaillées ou aux tests unitaires soufflés.
Cela m'indiquerait donc la description de votre premier test, si c'est aussi la raison pour laquelle vous utilisez Cucumber.
Il n’ya pas de problème majeur lors de la mise en oeuvre de tests comme la deuxième version, Cucumber peut le supporter. Il n’ya probablement pas beaucoup de types d’instruction à analyser. Mais vous pourriez finir par vous battre un peu contre le cadre de test, ou aller à l’encontre de votre logique d’utilisation du concombre en premier lieu.
En ce qui concerne une convention, je ne connais pas suffisamment de tests d’API REST en pratique pour pouvoir faire des commentaires, et aucun des tests que j’ai vu tester n’a utilisé Cucumber comme structure.
Mise à jour: en parcourant SO sur le sujet, j'ai trouvé un lien vers ceci: https://github.com/jayzes/cucumber-api-steps , qui ressemble davantage à votre deuxième format.
Il existe maintenant quelques bibliothèques pour les tests REST côté serveur avec concombre dans Ruby. Voici un couple:
La bibliothèque que j'ai utilisée pour les tests côté serveur REST avec concombre est Cucumber-API-Steps .
Voici comment j'écrirais votre test en utilisant 'concombre-api-étapes'} _ (Recommandé):
@success
Scenario: Successfully add to cart
Given I am logged in
When I send a POST request to “/cart/add” with the following:
| item | body |
Then the response status should be “200”
And the JSON response should have "success" with the text "true"
When I send a GET request to “/cart”
Then the response status should be “200”
And the JSON response should be "{'items': ['tv']}"
Et voici à quoi mes tests ressemblent en utilisant 'concombre-api-étapes':
@success
Scenario: Successfully log in
Given I am logged out
When I send a POST request to “/login” with:
| username | [email protected] |
| password | mypassword |
Then the response status should be “200”
And the JSON response should have "firstName" with the text "Katie"
Voici comment j'écrirais votre test en utilisant 'api concombre':
@success
Scenario: Successfully add to cart
Given I am logged in
When I send a POST request to “/cart/add”
And I set JSON request body to '{item: body}'
Then the response status should be “200”
And the response should have key “success” with value “true”
When I send a GET request to “/cart”
Then the response status should be “200”
And the response should follow "{'items': ['tv']}"
Et voici à quoi ressemblent mes tests avec 'concombre-api':
@success
Scenario: Successfully log in
Given I am logged out
When I send a POST request to “/login” with:
| username | [email protected] |
| password | mypassword |
Then the response status should be “200”
And the response should have key “firstName”
should have key “firstName” with value “Katie”
. La partie "avec valeur" n'a pas encore été faite.Une autre ressource est ici , mais elle est ancienne (2011).
Je recommanderais votre premier scénario.
De par ma propre expérience, j'estime personnellement que l'utilisation de BDD en tant que méthode de fourniture de logiciel constitue le plus grand avantage que vous obtenez lorsque vous mettez l'accent sur la valeur commerciale.
En d'autres termes, les scénarios doivent être des exemples du comportement souhaité par l'entreprise, plutôt que la mise en œuvre technique. Cela garantit que le développement est axé sur les objectifs de l'entreprise et que les produits livrables correspondent à ses attentes.
C'est ce qu'on appelle le développement externe.
Des tests supplémentaires du comportement du système peuvent et devraient être utilisés pour couvrir les exigences techniques, mais je pense que les efforts de dépense réduisent leur coût en langage naturel, ce qui prend souvent beaucoup de temps et est laborieux dans un grand nombre de scénarios.
Je recommande l'approche suivante:
1) Travaillez avec les BA et les OP pour développer des exemples du comportement qu'ils souhaitent utiliser à l'aide d'un langage non spécifique à la mise en œuvre (comme votre premier exemple).
2) Les ingénieurs les utilisent pour piloter le développement à partir d’une première approche de test, en les automatisant en tant que tests d’intégration - la majorité se trouvant en dessous du navigateur (par rapport à votre API REST par exemple) et les scénarios les plus fondamentaux également via le navigateur (si nécessaire). vous en développez un).
3) Les ingénieurs TDD le code de fonction avec les tests unitaires jusqu'à ce que les tests unitaires et les exemples BDD réussissent.
Je pense que le premier est meilleur. Je mettrais la technique dans les classes et les modules Ruby. E.g comme module cart.add (items) dans l'étape When et dans l'étape then put expect (cart.item) .to include ('items' => a_string_matching (item))
Grâce à cela, les classes et modules Ruby peuvent être réutilisés dans d'autres étapes de fonctionnalités. Par exemple, vous avez peut-être un autre scénario qui ajouterait plusieurs articles dans le panier, puis validerait le montant total.
Cependant, je pense que le second 1 peut en faire une caractéristique technique. Une requête d'entête ou de corps commune/globale similaire à E.g est attendue pour l'ensemble de l'api.
Voir ici: https://github.com/ctco/cukes-rest . Il fournit un DSL de concombre pour tester les API RESTful.