{"id":696,"date":"2018-08-21T21:05:07","date_gmt":"2018-08-21T20:05:07","guid":{"rendered":"http:\/\/louis.hatier.me\/blog\/?p=696"},"modified":"2018-08-31T12:36:34","modified_gmt":"2018-08-31T11:36:34","slug":"behat-bdd-php-symfony-selenium-jenkins","status":"publish","type":"post","link":"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/","title":{"rendered":"Behat : BDD avec PHP, Symfony, Selenium et Jenkins"},"content":{"rendered":"<p><a href=\"http:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/logo-behat.png\" data-rel=\"lightbox-gallery-RG7whcUl\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\" wp-image-697 alignleft\" src=\"http:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/logo-behat.png\" alt=\"Logo Behat\" width=\"255\" height=\"104\" srcset=\"https:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/logo-behat.png 490w, https:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/logo-behat-300x122.png 300w\" sizes=\"auto, (max-width: 255px) 100vw, 255px\" \/><\/a><a href=\"http:\/\/behat.org\/en\/latest\/\" target=\"_blank\" rel=\"noopener\">Behat<\/a> est un framework de test <a href=\"https:\/\/en.wikipedia.org\/wiki\/Behavior-driven_development\" target=\"_blank\" rel=\"noopener\">Behavior-Driven Development<\/a> pour PHP.<\/p>\n<p>Les tests \u00e9crits avec <a href=\"https:\/\/docs.cucumber.io\/gherkin\/\" target=\"_blank\" rel=\"noopener\">Gherkin<\/a> permettent une meilleure communication entre les d\u00e9veloppeurs et le business, et une meilleure fiabilit\u00e9 de l\u2019application.<\/p>\n<p>Behat est d\u00e9velopp\u00e9 en utilisant une grande partie des composants Symfony 2.<\/p>\n<p><!--more--><\/p>\n<blockquote><p>If you are a stakeholder, this is your proof that developers understand exactly how you want this feature to work. If you are a developer, this is your proof that the stakeholder expects you to implement this feature exactly in the way you\u2019re planning to implement it.<\/p><\/blockquote>\n<p>Les sc\u00e9narios des tests s\u2019\u00e9crivent dans ce qui s\u2019appelle des <em>features<\/em>.<br \/>\nUne feature = un fichier <code>xxxxx.feature<\/code><br \/>\nUn sc\u00e9nario contient habituellement les \u00e9tapes suivantes : Given \/ When \/ Then \/ And (alias de Then)<\/p>\n<p>Une traduction des mots clefs est disponible en fran\u00e7ais mais il pr\u00e9f\u00e9rable de s&rsquo;en tenir \u00e0 l&rsquo;anglais (des soucis peuvent survenir avec certains caract\u00e8res), si besoin :<\/p>\n<pre><code class=\"no-highlight\">sudo apt install cucumber\r\ncucumber --i18n fr\r\n<\/code><\/pre>\n<h2><\/h2>\n<h2>Exemple de feature<\/h2>\n<pre><code class=\"no-highlight\">Feature: Product basket\r\n  In order to buy products\r\n  As a customer\r\n  I need to be able to put interesting products into a basket\r\n \r\n  Scenario: Buying a single product under 10\u20ac\r\n    Given there is a \"Sith Lord Lightsaber\", which costs 5\u20ac\r\n    When I add the \"Sith Lord Lightsaber\" to the basket\r\n    Then I should have 1 product in the basket\r\n    And the overall basket price should be 9\u20ac\r\n<\/code><\/pre>\n<p>Il est possible d&rsquo;utiliser des table-like ou paragraphe en utilisant les PyString <code>\"\"\"<\/code>.<br \/>\nExemple avec un tableau :<\/p>\n<pre><code class=\"no-highlight\">Feature: Get users\r\n    In order to check users\r\n    As an admin\r\n    I must have a list of all of them\r\n \r\n    Scenario: List users\r\n        Given there are users:\r\n        | username      | password | email           |\r\n        | foo           | 123456   | foo@bar.com     |\r\n        | bar           | 22@222   | bar@foo.com     |\r\n        When I search the user \"foo\"\r\n        Then I should find it and get the email \"foo@bar.com\"\r\n<\/code><\/pre>\n<h2><\/h2>\n<h2>Tags<\/h2>\n<p>Il est possible de renseigner un tag au niveau de la feature, cela permet de pr\u00e9ciser des contextes seulement pour certains tags, de lancer les tests sur une liste de tags etc.<\/p>\n<pre><code class=\"no-highlight\">@backoffice\r\nFeature: Get users\r\n    In order to check users\r\n    ...\r\n<\/code><\/pre>\n<h2><\/h2>\n<h2>Installation<\/h2>\n<pre><code class=\"no-highlight\">composer require --dev behat\/behat\r\nvendor\/bin\/behat --init\r\n<\/code><\/pre>\n<p>La deuxi\u00e8me commande cr\u00e9er les fichiers d&rsquo;init de Behat.<\/p>\n<h2><\/h2>\n<h2>Coloration<\/h2>\n<p>Behat est nativement support\u00e9 par <a href=\"http:\/\/louis.hatier.me\/blog\/plugins-phpstorm\/\">PHPStorm<\/a>, pour\u00a0<a href=\"http:\/\/louis.hatier.me\/blog\/sublime-text\/\">Sublime Text<\/a>\u00a0il existe un <a href=\"https:\/\/github.com\/omissis\/sublime-behat-syntax\" target=\"_blank\" rel=\"noopener\">package<\/a>\u00a0afin d&rsquo;avoir la coloration syntaxique.<br \/>\n<a href=\"http:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/behat-sublime-text.png\" data-rel=\"lightbox-gallery-RG7whcUl\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-698\" src=\"http:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/behat-sublime-text.png\" alt=\"Behat Sublime Text\" width=\"493\" height=\"256\" srcset=\"https:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/behat-sublime-text.png 493w, https:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/behat-sublime-text-300x156.png 300w\" sizes=\"auto, (max-width: 493px) 100vw, 493px\" \/><\/a><br \/>\nLes valeurs en jaunes sont r\u00e9cup\u00e9r\u00e9es dans le code afin d&rsquo;\u00eatre test\u00e9es.<\/p>\n<h2><\/h2>\n<h2>Lancer les tests<\/h2>\n<p>Une fois les features cr\u00e9\u00e9es dans le dossier <code>features\/<\/code>, on peut lancer les tests :<\/p>\n<pre><code class=\"no-highlight\">vendor\/bin\/behat # toutes les features\r\nvendor\/bin\/behat features\/basket.feature # une feature en particulier\r\nvendor\/bin\/behat --tags backoffice # toutes les features qui ont le tag backoffice\r\n<\/code><\/pre>\n<p>Pour le moment pas grand chose \u00e0 tester, afin de cr\u00e9er automatiquement les fonctions et leurs arguments, voici la commande magique propos\u00e9e :<\/p>\n<pre><code class=\"no-highlight\">vendor\/bin\/behat --dry-run --append-snippets\r\n<\/code><\/pre>\n<p>Behat cr\u00e9er les fonctions comme ceci :<\/p>\n<pre><code class=\"php\">\/**\r\n * @Given there is a :arg1, which costs :arg2\u20ac\r\n *\/\r\npublic function thereIsAWhichCostsEur($arg1, $arg2)\r\n{\r\n    \r\n}\r\n\r\n\/**\r\n * @When I add the :arg1 to the basket\r\n *\/\r\npublic function iAddTheToTheBasket($arg1)\r\n{\r\n    \r\n}\r\n\r\n\/**\r\n * @Then I should have :arg1 product in the basket\r\n *\/\r\npublic function iShouldHaveProductInTheBasket($arg1)\r\n{\r\n    \r\n}\r\n\r\n\/**\r\n * @Then the overall basket price should be :arg1\u20ac\r\n *\/\r\npublic function theOverallBasketPriceShouldBeEur($arg1)\r\n{\r\n    \r\n}\r\n<\/code><\/pre>\n<p>Il ne reste plus qu\u2019\u00e0 remplir le contenu des fonctions avec du code m\u00e9tier, et d\u2019y ajouter quelques assertions PHPUnit.<\/p>\n<p>Exemple simple :<\/p>\n<pre><code class=\"php\">use Behat\\Behat\\Context\\Context;\r\nuse PHPUnit\\Framework\\Assert;\r\n\r\nclass BasketContext implements Context\r\n{\r\n    const COST_SHIPPING = 4;\r\n\r\n    private $products = [];\r\n    private $basket = [];\r\n\r\n    public function __construct()\r\n    {\r\n    }\r\n\r\n    \/**\r\n     * @Given there is a :arg1, which costs :arg2\u20ac\r\n     *\/\r\n    public function thereIsAWhichCostsEur($arg1, $arg2)\r\n    {\r\n        $product = new \\stdClass();\r\n        $product-&gt;name = $arg1;\r\n        $product-&gt;price = $arg2;\r\n        $this-&gt;products[$product-&gt;name] = $product;\r\n    }\r\n\r\n    \/**\r\n     * @When I add the :arg1 to the basket\r\n     *\/\r\n    public function iAddTheToTheBasket($arg1)\r\n    {\r\n        if (\\array_key_exists($arg1, $this-&gt;products)) {\r\n            $this-&gt;basket[] = $this-&gt;products[$arg1];\r\n        }\r\n    }\r\n\r\n    \/**\r\n     * @Then I should have :arg1 product in the basket\r\n     *\/\r\n    public function iShouldHaveProductInTheBasket($arg1)\r\n    {\r\n        Assert::assertGreaterThan(0, \\count($this-&gt;basket));\r\n    }\r\n\r\n    \/**\r\n     * @Then the overall basket price should be :arg1\u20ac\r\n     *\/\r\n    public function theOverallBasketPriceShouldBeEur($arg1)\r\n    {\r\n        $amount = self::COST_SHIPPING;\r\n        foreach ($this-&gt;basket as $product) {\r\n            $amount += $product-&gt;price;\r\n        }\r\n\r\n        Assert::assertEquals($arg1, $amount);\r\n    }\r\n}\r\n<\/code><\/pre>\n<p><script src=\"https:\/\/asciinema.org\/a\/XdmLdZ3HvMjal2Eg0v2YqASV0.js\" id=\"asciicast-XdmLdZ3HvMjal2Eg0v2YqASV0\" async data-cols=\"110\" data-rows=\"30\"><\/script><\/p>\n<h2><\/h2>\n<h2>Fichier Bootstrap<\/h2>\n<p>Le Bootstrap de Behat peut \u00eatre surcharg\u00e9.<\/p>\n<p>Par d\u00e9faut il est cr\u00e9\u00e9 ici : <code>features\/bootstrap\/FeatureContext.php<\/code><\/p>\n<p>Il est possible d&rsquo;ajouter des instructions \u00e0 des moments pr\u00e9cis du d\u00e9roulement des tests : BeforeScenario, BeforeFeature, Then, When etc.<\/p>\n<p>Exemple :<\/p>\n<pre><code class=\"php\">\/** @BeforeFeature *\/\r\npublic static function prepareForTheFeature()\r\n{\r\n    \/\/ clean database or do other preparation stuff\r\n}\r\n\r\n\/**\r\n * @BeforeScenario @backoffice\r\n *\/\r\npublic function createSchema()\r\n{\r\n    echo 'je passe ici';\r\n}\r\n\r\n\/** @Given we have some context *\/\r\npublic function prepareContext()\r\n{\r\n    \/\/ do something\r\n}\r\n\r\n\/** @When event occurs *\/\r\npublic function doSomeAction()\r\n{\r\n    \/\/ do something\r\n}\r\n\r\n\/** @Then something should be done *\/\r\npublic function checkOutcomes()\r\n{\r\n    \/\/ do something\r\n}\r\n<\/code><\/pre>\n<p>A noter que la fonction <code>createSchema<\/code> ne s\u2019ex\u00e9cutera que pour les features contenant le tag \u00ab\u00a0backoffice\u00a0\u00bb<\/p>\n<h2><\/h2>\n<h2>Affichage des r\u00e9sultats<\/h2>\n<p>Pour sortir le r\u00e9sum\u00e9 sur la sortie standard (console) et le r\u00e9sultat XML Junit dans un fichier <code>\/behat-output\/default.xml<\/code><\/p>\n<pre><code class=\"no-highlight\">vendor\/bin\/behat -f progress -o std -f junit -o behat-output --strict\r\n<\/code><\/pre>\n<h2><\/h2>\n<h2>Tests navigateurs<\/h2>\n<p>Afin de r\u00e9aliser des tests Selenium depuis Behat, il faut installer plusieurs outils, voici un combo possible : JAR Selenium standalone + chromedriver + Mink<br \/>\n<a href=\"http:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/behat-selenium-mink-chrome.jpg\" data-rel=\"lightbox-gallery-RG7whcUl\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-699\" src=\"http:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/behat-selenium-mink-chrome.jpg\" alt=\"Behat Selenium Mink Chrome\" width=\"464\" height=\"149\" srcset=\"https:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/behat-selenium-mink-chrome.jpg 781w, https:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/behat-selenium-mink-chrome-300x96.jpg 300w, https:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/behat-selenium-mink-chrome-768x247.jpg 768w\" sizes=\"auto, (max-width: 464px) 100vw, 464px\" \/><\/a><\/p>\n<p><a href=\"http:\/\/mink.behat.org\/en\/latest\/\" target=\"_blank\" rel=\"noopener\">Mink<\/a> est un outil open source, browser emulator \u00e9crit en PHP, l&rsquo;installation se faire via composer (Cf. next step).<\/p>\n<p>Il faut ensuite r\u00e9cup\u00e9rer le JAR Selenium sur le <a href=\"https:\/\/www.seleniumhq.org\/download\/\" target=\"_blank\" rel=\"noopener\">site officiel<\/a>, puis le <a href=\"http:\/\/chromedriver.storage.googleapis.com\/2.38\/chromedriver_linux64.zip\" target=\"_blank\" rel=\"noopener\">driver chrome<\/a> (lien \u00e0 modifier en fonction de la version en cours).<\/p>\n<p>Quelques lignes de commande afin d&rsquo;utiliser chromedriver :<\/p>\n<pre><code class=\"no-highlight\">unzip ~\/Downloads\/chromedriver_linux64.zip -d ~\/Downloads\r\nchmod +x ~\/Downloads\/chromedriver\r\nsudo mv -f ~\/Downloads\/chromedriver \/usr\/local\/share\/chromedriver\r\nsudo ln -s \/usr\/local\/share\/chromedriver \/usr\/local\/bin\/chromedriver\r\nsudo ln -s \/usr\/local\/share\/chromedriver \/usr\/bin\/chromedriver\r\n<\/code><\/pre>\n<p>Si tout s\u2019est bien pass\u00e9 il est maintenant possible de lancer Selenium de cette fa\u00e7on :<\/p>\n<pre><code class=\"no-highlight\">java -Dwebdriver.chromedriver=\/usr\/bin\/chromedriver -jar ~\/Downloads\/selenium-server-standalone-3.12.0.jar\r\n<\/code><\/pre>\n<p>Voici un exemple de feature qui va r\u00e9aliser les tests navigateurs, et qui fonctionne avec les directives natives Behat\/Mink et donc ne n\u00e9cessite pas de classe PHP !<\/p>\n<pre><code class=\"no-highlight\">@selenium\r\nFeature: User profile\r\n    I can see my name on my profile\r\n\r\n    Scenario: Dashboard page shows my name\r\n        Given I am on \"\/sign-in\"\r\n        Then I should see \"Sign in\"\r\n        When I fill in \"email\" with \"foo@bar.com\"\r\n        And I fill in \"password\" with \"123456\"\r\n        And I press \"submit\"\r\n        Then I should see \"Name: Foo\"\r\n<\/code><\/pre>\n<h2><\/h2>\n<h2>Int\u00e9gration \u00e0 Symfony<\/h2>\n<p>Installer les packages n\u00e9cessaires :<\/p>\n<pre><code class=\"no-highlight\">composer require --dev behat\/symfony2-extension\r\ncomposer require --dev behat\/mink\r\ncomposer require --dev behat\/mink-extension\r\ncomposer require --dev behat\/mink-browserkit-driver\r\ncomposer require --dev behat\/mink-selenium2-driver\r\n<\/code><\/pre>\n<p>Modifier le fichier de conf <code>behat.yml<\/code> g\u00e9n\u00e9r\u00e9 \u00e0 la racine du projet :<\/p>\n<pre><code class=\"no-highlight\">default:\r\n    suites:\r\n        default:\r\n            type: symfony_bundle\r\n            bundle: AppBundle\r\n            contexts:\r\n                - FeatureContext:\r\n                    mySymfonyParam: \"%%db_host%%\"\r\n                    doctrine: \"@doctrine\"\r\n                - AppBundle\\Features\\Context\\BasketContext:\r\n                    basket: \"@app.basket\"\r\n                - AppBundle\\Features\\Context\\UsersContext:\r\n    extensions:\r\n        Behat\\Symfony2Extension:\r\n            kernel:\r\n                env: \"dev\"\r\n                debug: \"true\"\r\n        Behat\\MinkExtension:\r\n            base_url: http:\/\/myapp.my.company\/web\/\r\n            browser_name: 'chrome'\r\n            sessions:\r\n                default:\r\n                    selenium2: ~\r\n<\/code><\/pre>\n<p>Dans l\u2019exemple ci-dessus on voit qu\u2019il est possible de passer en argument des param\u00e8tres et des services Symfony aux classes Behat.<\/p>\n<h2><\/h2>\n<h2>Int\u00e9gration \u00e0 Jenkins<\/h2>\n<p>Il est n\u00e9cessaire d&rsquo;abord de configurer le lancement de Selenium pour l&rsquo;utilisateur Linux qui va lancer les jobs (utilisateur jenkins en l\u2019occurrence).<br \/>\nCela peut se faire via une <a href=\"http:\/\/louis.hatier.me\/blog\/syntaxe-tache-cron-lisible\/\">CRON<\/a> qui ex\u00e9cutera le script suivant :<\/p>\n<pre><code class=\"no-highlight\">#!\/bin\/bash\r\n\r\nXvfb -ac :99 -screen 0 1280x1024x16 &amp;\r\nexport DISPLAY=:99\r\njava -Dwebdriver.chromedriver=\/usr\/bin\/chromedriver -jar ~\/behat\/selenium-server-standalone-3.12.0.jar\r\n<\/code><\/pre>\n<p>Et pour le crontab :<\/p>\n<pre><code class=\"no-highlight\">@reboot ~\/behat\/script-demarrage.sh\r\n<\/code><\/pre>\n<p>Pour v\u00e9rifier que Selenium se lance bien apr\u00e8s reboot, ex\u00e9cuter la commande suivante (le port 4444 doit \u00eatre visible) :<\/p>\n<pre><code class=\"no-highlight\">netstat -tln\r\n<\/code><\/pre>\n<p>Ensuite il suffit d\u2019ajouter l\u2019instruction Behat au <a href=\"http:\/\/louis.hatier.me\/blog\/jenkins-pipeline-et-blue-ocean\/\">Pipeline Jenkins<\/a> :<\/p>\n<pre><code class=\"groovy\">stage('Tests Behat') {\r\n    def path = pwd()\r\n    sh \"${path}\/vendor\/bin\/behat -f progress -o std --strict\"\r\n}\r\n<\/code><\/pre>\n<p>Si les tests \u00e9chouent, le job retournera un fail.<\/p>\n<div style=\"padding-bottom:20px; padding-top:10px;\" class=\"hupso-share-buttons\"><!-- Hupso Share Buttons - https:\/\/www.hupso.com\/share\/ --><a class=\"hupso_counters\" href=\"https:\/\/www.hupso.com\/share\/\"><img decoding=\"async\" src=\"https:\/\/static.hupso.com\/share\/buttons\/dot.png\" style=\"border:0px; padding-top:2px; float:left;\" alt=\"Share Button\"\/><\/a><script type=\"text\/javascript\">var hupso_services_c=new Array(\"twitter\",\"facebook_like\",\"pinterest\",\"email\",\"linkedin\");var hupso_counters_lang = \"en_US\";var hupso_image_folder_url = \"\";var hupso_url_c=\"\";var hupso_title_c=\"Behat%20%3A%20BDD%20avec%20PHP%2C%20Symfony%2C%20Selenium%20et%20Jenkins\";<\/script><script type=\"text\/javascript\" src=\"https:\/\/static.hupso.com\/share\/js\/counters.js\"><\/script><!-- Hupso Share Buttons --><\/div>","protected":false},"excerpt":{"rendered":"<p>Behat est un framework de test Behavior-Driven Development pour PHP. Les tests \u00e9crits avec Gherkin permettent une meilleure communication entre les d\u00e9veloppeurs et le business, et une meilleure fiabilit\u00e9 de l\u2019application. Behat est d\u00e9velopp\u00e9 en utilisant une grande partie des composants Symfony 2.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[68,2],"tags":[72,70,63,57,77,18],"class_list":["post-696","post","type-post","status-publish","format-standard","hentry","category-ci-cd","category-php","tag-behat","tag-jenkins","tag-open-source","tag-php","tag-selenium","tag-symfony"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.9 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Behat : BDD avec PHP, Symfony, Selenium et Jenkins - Melting Poutre<\/title>\n<meta name=\"description\" content=\"Faire du Behavior-Driven Development en PHP avec Behat\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Behat : BDD avec PHP, Symfony, Selenium et Jenkins - Melting Poutre\" \/>\n<meta property=\"og:description\" content=\"Faire du Behavior-Driven Development en PHP avec Behat\" \/>\n<meta property=\"og:url\" content=\"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/\" \/>\n<meta property=\"og:site_name\" content=\"Melting Poutre\" \/>\n<meta property=\"article:published_time\" content=\"2018-08-21T20:05:07+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-08-31T11:36:34+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/logo-behat.png\" \/>\n<meta name=\"author\" content=\"Louis Hatier\" \/>\n<meta name=\"twitter:label1\" content=\"\u00c9crit par\" \/>\n\t<meta name=\"twitter:data1\" content=\"Louis Hatier\" \/>\n\t<meta name=\"twitter:label2\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/\"},\"author\":{\"name\":\"Louis Hatier\",\"@id\":\"https:\/\/louis.hatier.me\/blog\/#\/schema\/person\/2f200d0368def135b452e65491c4dd11\"},\"headline\":\"Behat : BDD avec PHP, Symfony, Selenium et Jenkins\",\"datePublished\":\"2018-08-21T20:05:07+00:00\",\"dateModified\":\"2018-08-31T11:36:34+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/\"},\"wordCount\":665,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/louis.hatier.me\/blog\/#\/schema\/person\/2f200d0368def135b452e65491c4dd11\"},\"image\":{\"@id\":\"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/#primaryimage\"},\"thumbnailUrl\":\"http:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/logo-behat.png\",\"keywords\":[\"Behat\",\"Jenkins\",\"Open source\",\"PHP\",\"Selenium\",\"Symfony\"],\"articleSection\":[\"CI\/CD\",\"PHP\"],\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/\",\"url\":\"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/\",\"name\":\"Behat : BDD avec PHP, Symfony, Selenium et Jenkins - Melting Poutre\",\"isPartOf\":{\"@id\":\"https:\/\/louis.hatier.me\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/#primaryimage\"},\"thumbnailUrl\":\"http:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/logo-behat.png\",\"datePublished\":\"2018-08-21T20:05:07+00:00\",\"dateModified\":\"2018-08-31T11:36:34+00:00\",\"description\":\"Faire du Behavior-Driven Development en PHP avec Behat\",\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/#primaryimage\",\"url\":\"http:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/logo-behat.png\",\"contentUrl\":\"http:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/logo-behat.png\"},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/louis.hatier.me\/blog\/#website\",\"url\":\"https:\/\/louis.hatier.me\/blog\/\",\"name\":\"Melting Poutre\",\"description\":\"Du web et d&#039;autres choses\",\"publisher\":{\"@id\":\"https:\/\/louis.hatier.me\/blog\/#\/schema\/person\/2f200d0368def135b452e65491c4dd11\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/louis.hatier.me\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"fr-FR\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/louis.hatier.me\/blog\/#\/schema\/person\/2f200d0368def135b452e65491c4dd11\",\"name\":\"Louis Hatier\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/louis.hatier.me\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/cd8d107ede5a4ec340326655e74a00ca62b02e41a02442f961c36f085aa89942?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/cd8d107ede5a4ec340326655e74a00ca62b02e41a02442f961c36f085aa89942?s=96&d=mm&r=g\",\"caption\":\"Louis Hatier\"},\"logo\":{\"@id\":\"https:\/\/louis.hatier.me\/blog\/#\/schema\/person\/image\/\"},\"sameAs\":[\"https:\/\/louis.hatier.me\",\"https:\/\/www.linkedin.com\/in\/louishatier\/\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Behat : BDD avec PHP, Symfony, Selenium et Jenkins - Melting Poutre","description":"Faire du Behavior-Driven Development en PHP avec Behat","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/","og_locale":"fr_FR","og_type":"article","og_title":"Behat : BDD avec PHP, Symfony, Selenium et Jenkins - Melting Poutre","og_description":"Faire du Behavior-Driven Development en PHP avec Behat","og_url":"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/","og_site_name":"Melting Poutre","article_published_time":"2018-08-21T20:05:07+00:00","article_modified_time":"2018-08-31T11:36:34+00:00","og_image":[{"url":"http:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/logo-behat.png","type":"","width":"","height":""}],"author":"Louis Hatier","twitter_misc":{"\u00c9crit par":"Louis Hatier","Dur\u00e9e de lecture estim\u00e9e":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/#article","isPartOf":{"@id":"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/"},"author":{"name":"Louis Hatier","@id":"https:\/\/louis.hatier.me\/blog\/#\/schema\/person\/2f200d0368def135b452e65491c4dd11"},"headline":"Behat : BDD avec PHP, Symfony, Selenium et Jenkins","datePublished":"2018-08-21T20:05:07+00:00","dateModified":"2018-08-31T11:36:34+00:00","mainEntityOfPage":{"@id":"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/"},"wordCount":665,"commentCount":0,"publisher":{"@id":"https:\/\/louis.hatier.me\/blog\/#\/schema\/person\/2f200d0368def135b452e65491c4dd11"},"image":{"@id":"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/#primaryimage"},"thumbnailUrl":"http:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/logo-behat.png","keywords":["Behat","Jenkins","Open source","PHP","Selenium","Symfony"],"articleSection":["CI\/CD","PHP"],"inLanguage":"fr-FR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/","url":"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/","name":"Behat : BDD avec PHP, Symfony, Selenium et Jenkins - Melting Poutre","isPartOf":{"@id":"https:\/\/louis.hatier.me\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/#primaryimage"},"image":{"@id":"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/#primaryimage"},"thumbnailUrl":"http:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/logo-behat.png","datePublished":"2018-08-21T20:05:07+00:00","dateModified":"2018-08-31T11:36:34+00:00","description":"Faire du Behavior-Driven Development en PHP avec Behat","inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/"]}]},{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/louis.hatier.me\/blog\/behat-bdd-php-symfony-selenium-jenkins\/#primaryimage","url":"http:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/logo-behat.png","contentUrl":"http:\/\/louis.hatier.me\/blog\/wp-content\/uploads\/2018\/08\/logo-behat.png"},{"@type":"WebSite","@id":"https:\/\/louis.hatier.me\/blog\/#website","url":"https:\/\/louis.hatier.me\/blog\/","name":"Melting Poutre","description":"Du web et d&#039;autres choses","publisher":{"@id":"https:\/\/louis.hatier.me\/blog\/#\/schema\/person\/2f200d0368def135b452e65491c4dd11"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/louis.hatier.me\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"fr-FR"},{"@type":["Person","Organization"],"@id":"https:\/\/louis.hatier.me\/blog\/#\/schema\/person\/2f200d0368def135b452e65491c4dd11","name":"Louis Hatier","image":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/louis.hatier.me\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/cd8d107ede5a4ec340326655e74a00ca62b02e41a02442f961c36f085aa89942?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/cd8d107ede5a4ec340326655e74a00ca62b02e41a02442f961c36f085aa89942?s=96&d=mm&r=g","caption":"Louis Hatier"},"logo":{"@id":"https:\/\/louis.hatier.me\/blog\/#\/schema\/person\/image\/"},"sameAs":["https:\/\/louis.hatier.me","https:\/\/www.linkedin.com\/in\/louishatier\/"]}]}},"_links":{"self":[{"href":"https:\/\/louis.hatier.me\/blog\/wp-json\/wp\/v2\/posts\/696","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/louis.hatier.me\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/louis.hatier.me\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/louis.hatier.me\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/louis.hatier.me\/blog\/wp-json\/wp\/v2\/comments?post=696"}],"version-history":[{"count":14,"href":"https:\/\/louis.hatier.me\/blog\/wp-json\/wp\/v2\/posts\/696\/revisions"}],"predecessor-version":[{"id":744,"href":"https:\/\/louis.hatier.me\/blog\/wp-json\/wp\/v2\/posts\/696\/revisions\/744"}],"wp:attachment":[{"href":"https:\/\/louis.hatier.me\/blog\/wp-json\/wp\/v2\/media?parent=696"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/louis.hatier.me\/blog\/wp-json\/wp\/v2\/categories?post=696"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/louis.hatier.me\/blog\/wp-json\/wp\/v2\/tags?post=696"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}