Bienvenue dans cette série sur le développement d'applications Laravel à l'aide d'une approche de développement piloté par le comportement (BDD). Full stack BDD peut sembler compliqué et intimidant. Il y a autant de façons de le faire qu'il y a de développeurs.
Dans cette série, je vous expliquerai comment utiliser Behat et PhpSpec pour concevoir une application Laravel à partir de zéro..
Il existe de nombreuses ressources sur BDD en général, mais il est difficile de trouver du matériel spécifique à Laravel. Par conséquent, dans cette série, nous allons nous concentrer davantage sur les aspects liés à Laravel et moins sur les éléments généraux que vous pouvez lire sur de nombreux autres endroits..
Lors de la description du comportement, également connu sous le nom d'écriture d'histoires et de spécifications, nous utiliserons une approche externe. Cela signifie que chaque fois que nous construirons une nouvelle fonctionnalité, nous commencerons par écrire l'histoire utilisateur globale. Ceci est normalement du point de vue des clients ou des parties prenantes.
Qu'attendons-nous quand nous faisons cela??
Nous ne sommes pas autorisés à écrire du code avant d'avoir une étape rouge défaillante, par exemple, à moins de refactoriser le code existant..
Parfois, il sera nécessaire de résoudre de manière itérative une petite partie d’une histoire ou d’un reportage (deux mots que j’utilise indifféremment) sur laquelle nous travaillons. Cela impliquera souvent d'écrire des spécifications avec PhpSpec. Parfois, il faudra de nombreuses itérations au niveau de l'intégration ou de l'unité avant que toute l'histoire (au niveau de l'acceptation) ne passe. Tout cela semble très compliqué, mais ce n’est vraiment pas le cas. Je suis un grand partisan de l’apprentissage pratique, donc je pense que tout aura plus de sens une fois que nous commencerons à écrire du code..
Nous écrirons des histoires et des spécifications à quatre niveaux différents:
La plupart du temps, notre suite fonctionnelle servira de couche d'acceptation. La manière dont nous allons décrire nos fonctionnalités dans notre suite fonctionnelle sera très similaire à celle utilisée pour la rédaction d'histoires d'acceptation (à l'aide d'un framework de navigateur automatisé) et créerait ainsi beaucoup de duplication..
Tant que les histoires décrivent le comportement du point de vue du client, elles servent d'histoires d'acceptation. Nous allons utiliser Symfony DomCrawler pour tester la sortie de notre application. Plus tard dans la série, nous découvrirons peut-être que nous devons tester via un navigateur qui peut également exécuter JavaScript. Tester via le navigateur ajoute de nouvelles préoccupations, car nous devons nous assurer de charger l'environnement de test heure par heure lorsque la suite est exécutée..
Dans notre suite fonctionnelle, nous aurons accès à l’application Laravel, ce qui est très pratique. Tout d’abord, il est facile de différencier les environnements. Deuxièmement, le fait de ne pas passer par un navigateur accélère considérablement notre suite de tests. Chaque fois que nous voulons implémenter une nouvelle fonctionnalité, nous allons écrire une histoire dans notre suite fonctionnelle en utilisant Behat..
Notre suite d'intégration testera le comportement de la partie principale de notre application qui n'a pas nécessairement besoin d'accéder à Laravel. La suite d’intégration sera normalement un mélange d’histoires Behat et de spécifications PhpSpec.
Nos tests unitaires seront écrits en PhpSpec et testeront de petites unités isolées du noyau de l'application. Nos entités, objets de valeur, etc. auront tous des spécifications.
Tout au long de cette série, à partir du prochain article, nous allons construire un système de suivi du temps. Nous allons commencer par décrire le comportement de l'extérieur en écrivant des fonctionnalités de Behat. Le comportement interne de notre application sera décrit à l'aide de PhpSpec.
Ensemble, ces deux outils nous aideront à nous sentir à l'aise avec la qualité de l'application que nous construisons. Nous allons principalement écrire des fonctionnalités et des spécifications sur trois niveaux:
Dans notre suite fonctionnelle, nous allons analyser les réponses HTTP de notre application en mode sans tête, ce qui signifie que nous ne passerons pas par le navigateur. Cela facilitera l’interaction avec Laravel et permettra à notre suite fonctionnelle de servir de couche d’acceptation..
Plus tard, si nous avons une interface utilisateur plus compliquée et que nous devons également tester du JavaScript, nous pourrions ajouter une suite d'acceptation dédiée. Cette série est toujours en cours, alors n'hésitez pas à déposer vos suggestions dans la section commentaires.
Notez que pour ce tutoriel, je suppose que vous avez une nouvelle installation de Laravel (4.2) opérationnelle. De préférence, vous utilisez également Laravel Homestead, ce que j’ai utilisé lors de la rédaction de ce code..
Avant de commencer tout travail réel, assurons-nous que Behat et PhpSpec sont opérationnels. Tout d’abord, j’aime bien nettoyer un peu chaque fois que je lance un nouveau projet Laravel et supprime ce dont je n’ai pas besoin:
git rm -r app / tests / phpunit.xml CONTRIBUTING.md
Si vous supprimez ces fichiers, veillez à mettre à jour votre composer.json
déposer en conséquence:
"autoload": "classmap": ["app / orders", "app / controllers", "app / models", "app / database / migrations", "app / database / seed"],
Et bien sur:
$ composer dump-autoload
Nous sommes maintenant prêts à utiliser les outils BDD dont nous avons besoin. Il suffit d'ajouter un require-dev
section à votre composer.json
:
"require": "laravel / framework": "4.2. *", "require-dev": "behat / behat": "~ 3.0", "phpspec / phpspec": "~ 2.0", "phpunit / phpunit ":" ~ 4.1 ",
"Pourquoi mettons-nous en PHPUnit?" vous pourriez penser? Nous n'allons pas écrire de bons vieux cas de tests PHPUnit dans cette série, mais les assertions sont un outil pratique avec Behat. Nous verrons cela plus tard dans cet article en écrivant notre premier long métrage..
N'oubliez pas de mettre à jour vos dépendances après avoir modifié composer.json
:
$ update composer --dev
Nous avons presque terminé l'installation et la configuration. PhpSpec fonctionne hors de la boîte:
$ vendor / bin / phpspec run 0 specs 0 examples 0ms
Mais Behat doit faire un tour rapide avec le --init
option pour tout mettre en place:
$ vendor / bin / behat --init + d features - placez vos fichiers * .feature ici + d features / bootstrap - placez vos classes de contexte ici + f features / bootstrap / FeatureContext.php - placez vos définitions, transformations et points d'ancrage ici $ fournisseur / bin / behat Aucun scénario Aucune étape 0m0.14s (12.18Mb)
La première commande a créé un nouveau brillant FeatureContext
classe, où nous pouvons écrire les définitions d’étape nécessaires à nos fonctionnalités:
Écrire notre premier long métrage
Notre première fonctionnalité sera très simple: nous nous assurerons simplement que notre nouvelle installation de Laravel nous salue par un "Vous êtes arrivé". sur la page d'accueil. Je mets un peu bête
Donné
marche aussi,Étant donné que je suis connecté
, ce qui ne sert qu'à montrer à quel point il est facile d'interagir avec Laravel dans nos fonctionnalités.Techniquement, je classerais ce type de fonctionnalité comme un test fonctionnel, car il interagit avec le framework, mais il sert également de test d'acceptation, car nous ne verrions aucun résultat différent de l'exécution d'un test similaire via un outil de test de navigateur. Pour l'instant, nous allons nous en tenir à notre suite de tests fonctionnels.
Allez-y et créez un
bienvenue.feature
déposer et le mettre dansfonctionnalités / fonctionnel
:# features / functional / welcome.feature Feature: Développeur accueillant En tant que développeur Laravel Pour pouvoir lancer un nouveau projet de manière probante, je dois être accueilli à l'arrivée. Scénario: Développeur d'accueil sur la page d'accueil Étant donné que je suis connecté Lorsque je visite " devrait voir "Vous êtes arrivé."En mettant les fonctionnalités dans un
fonctionnel
répertoire, il nous sera plus facile de gérer nos suites ultérieurement. Nous ne voulons pas de fonctionnalités d'intégration de type ne nécessitant pas que Laravel attende la suite fonctionnelle lente.J'aime garder les choses en ordre, alors je pense que nous devrions avoir un contexte spécifique pour notre suite fonctionnelle qui puisse nous donner accès à Laravel. Vous pouvez simplement aller de l'avant et copier l'existant
FeatureContext
déposer et changer le nom de la classe enLaravelFeatureContext
. Pour que cela fonctionne, nous avons également besoin d'unbehat.yml
fichier de configuration.Créez-en un dans le répertoire racine de votre projet et ajoutez ce qui suit:
Par défaut: suites: fonctionnelles: chemins: [% paths.base% / features / functional] contextes: [LaravelFeatureContext]Je pense que le YAML ici est assez explicite. Notre suite fonctionnelle cherchera des caractéristiques dans la
fonctionnel
répertoire et les exécuter à travers leLaravelFeatureContext
.Si nous essayons d’exécuter Behat à ce stade, il nous dira d’implémenter les définitions d’étape nécessaires. Nous pouvons avoir Behat ajouter les méthodes d'échafaudage vides au
LaravelFeatureContext
avec la commande suivante:$ vendor / bin / behat --dry-run --append-snippets $ vendor / bin / behat Fonctionnalité: développeur accueillant En tant que développeur de Laravel Pour pouvoir lancer un nouveau projet de manière probante, je dois être accueilli par un scénario lointain: Scénario de bienvenue pour le développeur homepage # features / functional / welcome.feature: 6 Étant donné que je suis connecté # LaravelFeatureContext :: iAmLoggedIn () TODO: écrire la définition en attente Lorsque je visite "/" # LaravelFeatureContext :: iVisit () Alors je devrais voir "Vous êtes arrivé. " # LaravelFeatureContext :: iShouldSee () 1 scénario (1 en attente) 3 étapes (1 en attente, 2 ignorées) 0m0.28s (12.53Mb)Et maintenant, comme vous pouvez le constater à la sortie, nous sommes prêts à commencer à mettre en œuvre la première de nos étapes:
Étant donné que je suis connecté
.Le scénario de test PHPUnit fourni avec Laravel nous permet de faire des choses comme
$ this-> be ($ user)
, qui se connecte à un utilisateur donné. En fin de compte, nous souhaitons pouvoir interagir avec Laravel comme si nous utilisions PHPUnit. Nous allons donc écrire le code de définition d'étape "nous souhaitons l'avoir":/ ** * @Given Je suis connecté * / fonction publique iAmLoggedIn () $ user = new User; $ this-> be ($ user);Bien entendu, cela ne fonctionnera pas, car Behat n’a aucune idée du contenu spécifique à Laravel, mais je vais vous montrer dans quelques instants à quel point il est facile de faire en sorte que Behat et Laravel jouent bien ensemble..
Si vous jetez un oeil dans la source Laravel et trouvez le
Illuminate \ Foundation \ Testing \ TestCase
class, qui est la classe à partir de laquelle le scénario de test par défaut s'étend, vous verrez qu'à partir de Laravel 4.2, tout a été déplacé vers un trait. leApplicationTrait
est maintenant responsable du démarrage d'unApplication
exemple, la configuration d’un client HTTP et quelques méthodes d’aide, telles queêtre()
.C'est plutôt cool, principalement parce que cela signifie que nous pouvons simplement l'intégrer dans nos contextes Behat avec presque aucune configuration requise. Nous avons également accès à la
AssertionsTrait
, mais cela reste lié à PHPUnit.Lorsque nous tirons le trait, nous devons faire deux choses. Nous avons besoin d'un
installer()
méthode, comme celle duIlluminate \ Foundation \ Testing \ TestCase
classe, et nous avons besoin d'uncreateApplication ()
méthode, comme celle du test par défaut de Laravel. En fait, nous pouvons simplement copier ces deux méthodes et les utiliser directement.Il n’ya qu’une chose à noter: dans PHPUnit, la méthode
installer()
sera automatiquement appelé avant chaque test. Pour atteindre le même objectif dans Behat, nous pouvons utiliser le@AvantScenario
annotation.Ajoutez ce qui suit à votre
LaravelFeatureContext
:utilisez Illuminate \ Foundation \ Testing \ ApplicationTrait; / ** * Classe de contexte Behat. * / class LaravelFeatureContext implémente SnippetAcceptingContext / ** * Responsable de la fourniture d'une instance d'application Laravel. * / use ApplicationTrait; / ** * Initialise le contexte. * * Chaque scénario obtient son propre objet de contexte. * Vous pouvez également transmettre des arguments arbitraires au constructeur de contexte via behat.yml. * / fonction publique __construct () / ** * @BeforeScenario * / fonction publique setUp () if (! $ this-> app) $ this-> refreshApplication (); / ** * Crée l'application. * * @return \ Symfony \ Component \ HttpKernel \ HttpKernelInterface * / fonction publique createApplication () $ unitTesting = true; $ testEnvironment = 'testing'; return require __DIR __. '/… /… /bootstrap/start.php';Assez facile, et regardez ce que nous obtenons lorsque nous exécutons Behat:
$ vendor / bin / behat Fonctionnalité: Développeur accueillant En tant que développeur Laravel Pour pouvoir lancer un nouveau projet avec sérénité, je dois être salué à l’arrière du scénario. Scénario: Développeur saluant le site Internet # features / functional / welcome.feature: 6 Étant donné que je suis connecté # LaravelFeatureContext :: iAmLoggedIn () Quand je visite "/" # LaravelFeatureContext :: iVisit () TODO: écrire une définition en attente Alors je devrais voir "Vous êtes arrivé." # LaravelFeatureContext :: iShouldSee () 1 scénario (1 en attente) 3 étapes (1 réussie, 1 en attente, 1 ignorée) 0m0.73s (17.92Mb)Une première étape verte, ce qui signifie que notre configuration fonctionne!
Ensuite, nous pouvons mettre en œuvre le
Quand je visite
étape. Celui-ci est super facile, et nous pouvons simplement utiliser leappel()
méthode que leApplicationTrait
fournit. Une ligne de code nous y mènera:/ ** * @Lorsque je visite: uri * / fonction publique iVisit ($ uri) $ this-> call ('GET', $ uri);La dernière étape,
Alors je devrais voir
, prend un peu plus et nous devons tirer dans deux dépendances. Nous aurons besoin de PHPUnit pour l'assertion et nous aurons besoin de Symfony DomCrawler pour rechercher "Vous êtes arrivé". texte.Nous pouvons l'implémenter comme ceci:
utilisez PHPUnit_Framework_Assert en tant que PHPUnit; utiliser Symfony \ Component \ DomCrawler \ Crawler;… / ** * @Alors je devrais voir: text * / public function iShouldSee ($ text) $ crawler = new Crawler ($ this-> client-> getResponse () -> getContent ()); PHPUnit :: assertCount (1, $ crawler-> filterXpath ("// text () [. = '$ Text']"));C'est à peu près le même code que vous écririez si vous utilisiez PHPUnit. le
filterXpath ()
une partie est un peu déroutante et nous ne nous en soucierons pas maintenant, car elle sort un peu du cadre de cet article. Croyez-moi juste que ça marche.Lancer Behat une dernière fois est une bonne nouvelle:
$ vendor / bin / behat Fonctionnalité: Développeur accueillant En tant que développeur Laravel Pour pouvoir lancer un nouveau projet avec sérénité, je dois être salué à l’arrière du scénario. Scénario: Développeur saluant le site Internet # features / functional / welcome.feature: 6 Étant donné que je suis connecté # LaravelFeatureContext :: iAmLoggedIn () Quand je visite "/" # LaravelFeatureContext :: iVisit () Alors je devrais voir "Vous êtes arrivé." # LaravelFeatureContext :: iShouldSee () 1 scénario (1 réussi) 3 étapes (3 passés) 0m0.82s (19.46Mb)La fonctionnalité fonctionne comme prévu et le développeur est accueilli à son arrivée..
Conclusion
Le complet
LaravelFeatureContext
devrait maintenant ressembler à ceci:app) $ this-> refreshApplication (); / ** * Crée l'application. * * @return \ Symfony \ Component \ HttpKernel \ HttpKernelInterface * / fonction publique createApplication () $ unitTesting = true; $ testEnvironment = 'testing'; return require __DIR __. '/… /… /bootstrap/start.php'; / ** * @Given Je suis connecté * / fonction publique iAmLoggedIn () $ user = new User; $ this-> be ($ user); / ** * @When je visite: uri * / fonction publique iVisit ($ uri) $ this-> call ('GET', $ uri); / ** * @Alors je devrais voir: text * / public function iShouldSee ($ text) $ crawler = new Crawler ($ this-> client-> getResponse () -> getContent ()); PHPUnit :: assertCount (1, $ crawler-> filterXpath ("// text () [. = '$ Text']"));Nous disposons désormais d'une base vraiment intéressante sur laquelle nous pourrons développer notre nouvelle application Laravel utilisant BDD. J'espère vous avoir prouvé à quel point il est facile de faire jouer ensemble Laravel et Behat.
Nous avons abordé de nombreux sujets différents dans ce premier article. Ne vous inquiétez pas, nous examinerons plus en profondeur la suite de la série. Si vous avez des questions ou des suggestions, s'il vous plaît laissez un commentaire.