{"id":5364,"date":"2018-07-25T11:09:21","date_gmt":"2018-07-25T09:09:21","guid":{"rendered":"https:\/\/blog.nevercodealone.de\/?p=5364"},"modified":"2019-04-24T14:54:35","modified_gmt":"2019-04-24T12:54:35","slug":"codeception-tutorial","status":"publish","type":"post","link":"https:\/\/blog.nevercodealone.de\/codeception-tutorial\/","title":{"rendered":"Codeception Tutorial &#8211; Automatisierte Tests f\u00fcr ein besseres Leben"},"content":{"rendered":"<p>Gute Codeception Tests finden Bugs und sparen ein Drittel der Entwicklungszeit. Aber es gibt viele Vorurteile und Bedenken diese einzusetzen und in der Entwicklung zu ber\u00fccksichtigen. Das Projekt w\u00e4re zu klein, <a href=\"https:\/\/blog.nevercodealone.de\/softwarequalitaet-wird-nicht-bezahlt\/\">der Kunde w\u00fcrde das nicht zahlen<\/a>, die Entwickler Ressourcen w\u00fcrden nicht ausreichen, der Ausreden Katalog in dem Bereich Testing ist riesig. Tats\u00e4chlich fehlt es ganz einfach an Know How bei den Entwicklern und Aufkl\u00e4rung beim Kunden. Gute Tests bieten einen Wettbewerbsvorteil, der vor allem beim Stichpunkt <a href=\"https:\/\/nevercodealone.de\/employer-branding\/\" target=\"_blank\" rel=\"noopener noreferrer\">Employer Branding<\/a> auch eine zunehmend wichtige Rolle im Arbeitsmarkt spielt. In diesem Artikel zeige ich den praktischen Einsatz von Acceptance-, API- und PHPUnit-Tests in einem Symfony PHP Projekt mit dem Codeception Framework. Die Tests lassen sich dabei in allen PHP Projekten einsetzen.<br \/>\n<iframe loading=\"lazy\" width=\"560\" height=\"315\" src=\"https:\/\/www.youtube.com\/embed\/8e0FcZVPK2Y\" frameborder=\"0\" allow=\"autoplay; encrypted-media\" allowfullscreen><\/iframe><\/p>\n<h2>Das Agenturgesch\u00e4ft ist nicht mehr von 2010<\/h2>\n<p>Leider hatte sich das Agenturgesch\u00e4ft viele Jahre auf den Verkauf von Stunden spezialisiert. \u201cWenn der Kunde FTP-Deployment haben will, dann mu\u00df er das auch zahlen\u201d, so eine g\u00e4ngige Ansicht, die leider viel zu oft vertreten wird. In dem Kontext werden Entwickler auch immer dazu angehalten nicht innovativ zu sein, weil man hier ja viele Dinge einzeln verkaufen k\u00f6nnte, die man sonst in einem Abwasch erledigt h\u00e4tte. So wurde doch tats\u00e4chlich \u00fcber viele Jahre an Software-Qualit\u00e4t ganz bewusst gespart. Und was mit den Mittelstandskunden \u00fcber Jahre gut geklappt hat, weil hier die Webseite auch immer ein f\u00fcnftes Rad am Wagen war, \u00e4ndert sich auf einmal ganz schnell bei modernen Startups, die mit \u00fcppigen Budgets zuverl\u00e4ssige und skalierbare Software wollen und ganz konkrete ziele verfolgen. Und das ist der Grund warum Software-Qualit\u00e4t auf einmal im Rennen ist. In <a href=\"https:\/\/blog.nevercodealone.de\/versprechen-in-vorstellungsgespraechen\/\">Stellenausschreibungen steht man auch h\u00e4ufiger schon etwas von Continuous Integration<\/a>. Hier mu\u00df man sich als Entwickler und Arbeitgeber in der Evolution anpassen oder stehen bleiben.<\/p>\n<h2>Automate all the things &#8211; Es ist in deiner Natur<\/h2>\n<div id=\"attachment_5377\" style=\"width: 310px\" class=\"wp-caption alignleft\"><a href=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/automate-all-the-things.jpg\" data-rel=\"penci-gallery-image-content\" ><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-5377\" class=\"wp-image-5377 size-medium\" src=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/automate-all-the-things-300x300.jpg\" alt=\"Automate all the things\" width=\"300\" height=\"300\" srcset=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/automate-all-the-things-300x300.jpg 300w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/automate-all-the-things-150x150.jpg 150w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/automate-all-the-things-585x585.jpg 585w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/automate-all-the-things.jpg 640w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-5377\" class=\"wp-caption-text\">Automate all the things<\/p><\/div>\n<p>Es liegt in der Natur des Programmierers Dinge zu automatisieren. Das ist unsere innere Passion und Leidenschaft. Leider haben das viele von uns verlernt und eine kommende Generation nicht vorgelebt bekommen. Das ist nicht gut f\u00fcr die PHP Szene und ihren Stellenwert Gerade f\u00fcr <a href=\"https:\/\/blog.nevercodealone.de\/softwarequalitaet-webdevelopment\/\">Software-Qualit\u00e4t<\/a> mu\u00df man aber offen f\u00fcr sein und sie als Lebenseinstellung nehmen. Das bedeutet im wesentlichen schneller zu werden und auch Wert auf einen gewissen Stil zu legen. Das f\u00e4ngt mit <a href=\"https:\/\/blog.nevercodealone.de\/phpstorm-2017-1-parameter-name-hints-konfiguration\/\">Shortcuts in PHPStorm<\/a> an und setzt sich \u00fcber den generellen Einsatz von Tools fort. Zum Beispiel als Terminal oh-my-zsh einzusetzen und mit den Plugins z oder jump in das Projekt zu navigieren und dort mit \u201cpstorm .\u201d PhPStorm zu \u00f6ffnen ist eine Art sich schneller zwischen einzelnen Projekten zu bewegen. Genauso kann man hier \u00fcbrigens auch Sourcetree \u00f6ffnen. Das kann man allerdings auch abschaffen, da die Integration in PhPStorm echt super ist und man auf dem Terminal auch nicht alles verlernen sollte.<\/p>\n<h2>Codeception Acceptance Test &#8211; Schnell eine sehr hohe Testabdeckung<\/h2>\n<p>Generell sind Codeception Acceptance Tests eine zuverl\u00e4ssige Methode schnell eine sehr hohe Testabdeckung in einem Projekt zu erzielen. Das Frontend ist bekanntlich die Summe aller Prozesse. Betrachten wir zum Beispiel die Autovervollst\u00e4ndigung in einer Suche, so wird hier schon sichergestellt, da\u00df es im Backend, Frontend und der Datenbank keine Fehler gibt. Und das schafft man schon mit unter 10 Zeilen Quellcode f\u00fcr den Test. Und Hand aufs Herz, wer testet jedes Mal die Suche manuell? Nat\u00fcrlich kann die bei egal welcher \u00c4nderung im Frontend oder Backend wegfliegen. Wird schon einer merken &#8211; wohl kaum &#8211; ich kenne ein Megamenu in einem Shop, das war 3 Wochen lang weg und das ist ein Marktf\u00fchrer.<\/p>\n<h2>Zwei Tests Codeception Frontend Tests als Beispiel<\/h2>\n<p>Das Formular<br \/>\nIm wesentlichen besteht die Seite von Never Code Alone aus einigen Landingpages, die gezielt f\u00fcr Marketing Kampagnen eingesetzt werden. Hier gibt es einen Kontakt Formular Slider. W\u00fcrde das Formular nicht funktionieren es w\u00e4re f\u00fcr mich eine absolute Katastrophe. Deshalb teste ich es praktisch immer. Wirklich? Auch wenn ich nur die Headline \u00e4ndere? Was soll da schon passieren? War ich vielleicht zwischenzeitlich in anderen Branches und sind \u00c4nderungen an der Datenbank oder Composer im Hintergrund passiert. Kurz gesagt ich kann meiner lokalen Entwicklungsumgebung nicht vertrauen.<\/p>\n<pre><code class=\"php\">\r\nclass contactCest\r\n{\r\npublic function _before(AcceptanceTester $I)\r\n{\r\n$I-&gt;amOnPage('\/nca-paas-startup\/');\r\n$I-&gt;wait(5);\r\n$I-&gt;waitForElement('#setting &gt; i');\r\n}\r\n\r\n\/\/ tests\r\npublic function openContactFormAndSentWithValidData(AcceptanceTester $I)\r\n{\r\n$inputData = [\r\n'name' =&gt; 'Roland',\r\n'email' =&gt; 'rg-' . time() . '@gmail.com',\r\n'message' =&gt; 'Testify right here'\r\n];\r\n\r\n$I-&gt;click('#setting &gt; i');\r\n$I-&gt;wait(1);\r\n$I-&gt;selectOption('#reason', 'lunch');\r\n$I-&gt;fillField('#namefield', $inputData['name']);\r\n$I-&gt;fillField('#emailfield', $inputData['email']);\r\n$I-&gt;fillField('#phone', '112');\r\n$I-&gt;fillField('#messagefield', $inputData['message']);\r\n\r\n$I-&gt;click('#nca-form &gt; div &gt; div.button &gt; button');\r\n$I-&gt;waitForText('Danke wir melden uns');\r\n\r\n$inputData['message'] = 'lunch|112|Testify right here';\r\n\r\n$I-&gt;seeInDatabase(\r\n'message',\r\n$inputData\r\n);\r\n}\r\n}\r\n<\/code><\/pre>\n<div id=\"attachment_5378\" style=\"width: 310px\" class=\"wp-caption alignleft\"><a href=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-acceptance-database.png\" data-rel=\"penci-gallery-image-content\" ><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-5378\" class=\"wp-image-5378 size-medium\" src=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-acceptance-database-300x146.png\" alt=\"Codeception Acceptance Database\" width=\"300\" height=\"146\" srcset=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-acceptance-database-300x146.png 300w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-acceptance-database-768x375.png 768w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-acceptance-database-1024x500.png 1024w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-acceptance-database-585x286.png 585w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-acceptance-database.png 1100w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-5378\" class=\"wp-caption-text\">Codeception Acceptance Database<\/p><\/div>\n<p>Der eigentliche Test hat hier gerade einmal 25 Zeilen und deckt das Javascript, Routing, den AJAX Call und den richtigen Eintrag in der Datenbank sicher. Erst einmal m\u00f6chte ich aber auf einige dr\u00e4ngende Fragen eingehen. Die Zeit ist hier nicht repr\u00e4sentativ und dennoch schneller als ich mit Maus und Browser einen test durchf\u00fchren w\u00fcrde. Hier f\u00e4hrt immerhin ein Chrome auf meinem Mac hoch. Das dauert beim ersten Start eben ein wenig. Die hier verwendeten HTML Tags k\u00f6nnen in ein Page Objekt ausgelagert werden und so zentral oder auch pro Projekt verwaltet werden. Das zeige ich auch in dem zweiten Test. Die Tests k\u00f6nnen also in unterschiedlichen Projekten wiederverwendet werden. Wenn man data-q Attribute nutzt, dann ist man auch unabh\u00e4ngig von der HTML Struktur und wesentlich stabiler. Ja nat\u00fcrlich k\u00f6nnte hier auch das Errorhandling im Formular getestet werden, aber ehrlich gesagt gibt es das Feature gar nicht \ud83d\ude09 Und nat\u00fcrlich k\u00f6nnte man auch einen Mailcatcher anbinden und den Versand kontrollieren.<\/p>\n<p>Hier sind zwei statische Waits eingebaut worden. Einmal mit 5 und einmal mit 1 Sekunde. Die 5 Sekunden liegen haupts\u00e4chlich an dem Erstaufruf der Seite im Develop Modus. Hier ist ja ein Spinning Loader im Einsatz. Der liegt \u00fcber allem. Ich k\u00f6nnte jetzt korrekterweise auf dessen verschwinden und die Sichtbarkeit des Openers warten. Parallax Seiten haben aber \u201cEigenschaften\u201d und deshalb sind diese beiden Stellen hier kurzfristig die stabilste L\u00f6sung. Wie man es richtig macht steht auch im zweiten Test.<\/p>\n<p>Ich setze ein Array mit inputData auf. Diese Array kann ich genauso auch immer in der Datenbank suchen. Danach \u00f6ffne ich mit einem Klick den Kontakt Slider und f\u00fclle alle Felder aus. Dann wird das Formular abgeschickt und \u00fcber einen AJAX-Request verarbeitet. Bei Erfolg kommt dann ein Text zur\u00fcck. Das Beispiel zeigt, wie einfach es ist auf einen Text zu warten. Da die Message alle Informationen des Formulars enth\u00e4lt mu\u00df ich diese f\u00fcr die \u00dcberpr\u00fcfung \u00fcberschreiben. Hier k\u00f6nnte die Message noch in eine Variable und auch geschaut werden, ob es nur einen Datensatz mit dem konkreten Angaben gibt. Das ist f\u00fcr mich an der Stelle aber nicht relevant, da ich hier keine Spam Protection kontrolliere, sondern tats\u00e4chlich \u201cOpen contact form and sent with valid data\u201d. Der Name vom Test wird also auf der CLI und f\u00fcr den Report richtig aufgel\u00f6st.<\/p>\n<h2>Der Slider Test<\/h2>\n<pre><code class=\"php\">\r\nclass servicesCest\r\n{\r\n    public function _before(AcceptanceTester $I)\r\n    {\r\n        $I-&gt;amOnPage('\/');\r\n        $I-&gt;waitForPageLoad();\r\n    }\r\n\r\n    public function navigationMatchesAnchors(AcceptanceTester $I, startpage $startpage)\r\n    {\r\n        $anchors = $I-&gt;grabMultiple($startpage::$servicesNavigation, 'href');\r\n        $contentItems = $I-&gt;grabMultiple($startpage::$servicesItems, 'id');\r\n\r\n        foreach ($anchors as $key =&gt; $anchor) {\r\n            $anchorValue = explode('#', $anchor)[1];\r\n            $I-&gt;assertEquals($anchorValue, $contentItems[$key]);\r\n        }\r\n    }\r\n\r\n    public function onlyFirstContentElementIsVisibleOnStart(AcceptanceTester $I, startpage $startpage)\r\n    {\r\n        $contentItems = $I-&gt;grabMultiple($startpage::$servicesItems, 'id');\r\n\r\n        $first = true;\r\n        foreach ($contentItems as $contentItem) {\r\n            \/\/ First item is visible\r\n            if($first) {\r\n                $I-&gt;seeElement('#' . $contentItem);\r\n                $first = false;\r\n                continue;\r\n            }\r\n\r\n            $I-&gt;cantSeeElement('#' . $contentItem);\r\n        }\r\n    }\r\n\r\n    public function onClickElementIsVisibleAndAllOthersNot(AcceptanceTester $I, startpage $startpage)\r\n    {\r\n        $I-&gt;scrollTo($startpage::$serviceSection);\r\n\r\n        $anchors = $I-&gt;grabMultiple($startpage::$servicesNavigation, 'href');\r\n        $contentItems = $I-&gt;grabMultiple($startpage::$servicesItems, 'id');\r\n\r\n        array_shift($anchors);\r\n        $firstItem = array_shift($contentItems);\r\n\r\n        $I-&gt;seeElement('#' . $firstItem);\r\n\r\n        foreach ($anchors as $key =&gt; $anchor)\r\n        {\r\n            $I-&gt;click('\/\/*[@data-q=\"services-navigation\"]\/li[' . ($key + 2) . ']\/div\/a');\r\n            $I-&gt;waitForElementVisible('#' . $contentItems[$key]);\r\n\r\n            $actualContentItems = $I-&gt;grabMultiple($startpage::$servicesItems, 'id');\r\n            foreach ($actualContentItems as $actualContentItem) {\r\n                if($actualContentItem === $contentItems[$key]) {\r\n                    continue;\r\n                }\r\n\r\n                $I-&gt;cantSeeElement('#' . $actualContentItem);\r\n            }\r\n        }\r\n\r\n        $I-&gt;click('\/\/*[@data-q=\"services-navigation\"]\/li[1]\/div\/a');\r\n        $I-&gt;waitForElementVisible('#' . $firstItem);\r\n    }\r\n}\r\n<\/code><\/pre>\n<h2>Der Multislider ist das wichtigste Element f\u00fcr das Marketing<\/h2>\n<div id=\"attachment_5380\" style=\"width: 310px\" class=\"wp-caption alignleft\"><a href=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-run-service-cest.jpg\" data-rel=\"penci-gallery-image-content\" ><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-5380\" class=\"wp-image-5380 size-medium\" src=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-run-service-cest-300x251.jpg\" alt=\"Codeception run ServiceCest\" width=\"300\" height=\"251\" srcset=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-run-service-cest-300x251.jpg 300w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-run-service-cest-768x642.jpg 768w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-run-service-cest-1024x856.jpg 1024w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-run-service-cest-585x489.jpg 585w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-run-service-cest.jpg 1129w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-5380\" class=\"wp-caption-text\">Codeception run ServiceCest<\/p><\/div>\n<p>Der Multislider von Never Code Alone ist ein komplexes Content Element und auch das wichtigste Tool beim Marketing. Hier wird das Leistuingsangebot der Never Code Alone Events abgebildet und potentielle Kunden werden im Rahmen der Telefon-Aquise an diese Stelle gef\u00fchrt. Aber auch auf Veranstaltungen und beim Kunden wird mit genau diesem Element gearbeitet. Hier kann man sehr einfach konkrete Beispiele zu den einzelnen Leistungspunkten herausstellen. Video-Marketing, Socialmedia-Marketing, die Vor- und Nachberichte werden hier als Pakete abgebildet und zahlreiche Beispiele gezeigt. Aber da es hier kein CMS gibt und alle Inhalte per Copy&amp;Pase manuell gepflegt werden ist er auch sehr fehleranf\u00e4llig.<\/p>\n<h2>Die Features des Multi Sliders<\/h2>\n<div id=\"attachment_5381\" style=\"width: 310px\" class=\"wp-caption alignleft\"><a href=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-step-output-command-line.png\" data-rel=\"penci-gallery-image-content\" ><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-5381\" class=\"wp-image-5381 size-medium\" src=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-step-output-command-line-300x112.png\" alt=\"Codeception Step Output Command Line\" width=\"300\" height=\"112\" srcset=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-step-output-command-line-300x112.png 300w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-step-output-command-line.png 428w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-5381\" class=\"wp-caption-text\">Codeception Step Output Command Line<\/p><\/div>\n<p>Der Multi Slider ist ein Slider mit mehreren Kategorien. Die Kategorien bilden sich in einer seitlichen Navigation ab. Als Standard ist bei einem ersten Seitenaufruf das erste Element sichtbar und alle anderen sind nicht sichtbar. W\u00e4hlt man dann als User eine Slider Kategorie aus der Navigation aus wird diese sichtbar und alle anderen unsichtbar. Die einzelnen Slides lassen sich dann auf Klick navigieren.<\/p>\n<h2>Die Slider Tests im Detail<\/h2>\n<p>navigationMatchesAnchors<br \/>\nMit Codeception grapMultiple kann man mehrere Elemente in Array speichern. In diesem Test sind die Selektoren bereits ausgelagert. Die Assertion schaut, ob die Anker zu den Content Items passen. Der Test ist mit seinen 10 Zeilen sehr \u00fcberschaubar, zeigt aber Fehler einfacher an, als der eigentliche Klick Test. So wird ein einfacher Syntax Bug sehr schnell erkannt.<\/p>\n<p>onlyFirstContentElementIsVisibleOnStart<br \/>\nHier wird gepr\u00fcft, ab der erste Slider sichtbar und alle anderen unsichtbar sind, wenn man die Seit einfach nur aufruft. Das ist der erste Zustand des Sliders. Codeception seeElement und cantSeeElement ber\u00fccksichtigen den CSS Status und auch Layer, die dar\u00fcber liegen k\u00f6nnten. Es geht hier allerdings nicht um den sichtbaren Bereich im Browser.<\/p>\n<p>onClickElementIsVisibleAndAllOthersNot<br \/>\nHier werden alle Elemente mit der Maus geklickt. Das bedeutet man mu\u00df erstmal an die richtige Stelle scrollen. Dann nimmt man sich wieder die Anker und ContentItems und interiert dar\u00fcber. Vorher wird ebenfalls geguckt, ob das erste Element aktiv ist. Das passiert hier schon mit data-q Attributen. Dabei wird geschaut, ob alle anderen Elemente unsichtbar sind. Am Ende wird dann auch nochmal geschaut, ob das erste Element auch wieder funktioniert.<\/p>\n<h2>Check Slide Links \u2013 Performance ist immer wichtig<\/h2>\n<div id=\"attachment_5382\" style=\"width: 310px\" class=\"wp-caption alignleft\"><a href=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/finish-codeception-tests-genereate-report.png\" data-rel=\"penci-gallery-image-content\" ><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-5382\" class=\"wp-image-5382 size-medium\" src=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/finish-codeception-tests-genereate-report-300x29.png\" alt=\"Finish Codeception Tests Genereate Report\" width=\"300\" height=\"29\" srcset=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/finish-codeception-tests-genereate-report-300x29.png 300w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/finish-codeception-tests-genereate-report-585x57.png 585w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/finish-codeception-tests-genereate-report.png 750w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-5382\" class=\"wp-caption-text\">Finish Codeception Tests Genereate Report<\/p><\/div>\n<p>Mit Codeception Acceptance Tests versetzt man sich in den User. Hier eine Idee f\u00fcr einen m\u00f6glichen weiteren Test. Alle Inhalte aus dem Slider verlinken zu weiterf\u00fchrenden Informationen, die f\u00fcr den User entscheidend sind, damit er zu einem m\u00f6glichen Kunden wird. Das Ziel des Tests w\u00e4re es also alle diese Links zu validieren. Wir wollen hier nicht testen, ob die Links alle geklickt werden k\u00f6nnen. Nat\u00fcrlich gibt es immer unterschiedliche Wege als Programmierer ein Ziel zu erreichen. Aber Tests m\u00fcssen auch immer schnell sein. Sonst werden sie nicht mehr ausgef\u00fchrt. Wir holen also alle Links aus den Slider Items und checken hier kurz mit Curl auf 200. Nat\u00fcrlich kann man das auch auf alle Links anwenden. Bravo diesen Test kann man auf allen Seiten und in allen Projekten sinnvoll einsetzen.<\/p>\n<h2>API Tests<\/h2>\n<p>Codeception ist ein <a href=\"https:\/\/codeception.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">Test Framework<\/a>. Das hei\u00dft es gibt einem Methoden zur Verf\u00fcgung, die komplexe Prozesse wrappen. Die leichte Syntax zeichnet das Framework aus. Da APIs immer mehr in Form von Microservices Anwendung finden sind Tests hier eine ganz wichtige Sache.<\/p>\n<p>Der Test<\/p>\n<pre><code class=\"php\">\r\nclass messagesCest\r\n{\r\n    public function addMessageWithValidNameAndEmail(ApiTester $I)\r\n    {\r\n        $microtime = microtime();\r\n\r\n        $name = 'test';\r\n        $email = 'test' . time() . '@ify.com';\r\n        $message = 'CC message:' . $microtime;\r\n\r\n        $messagePost = [\r\n            'name'    =&gt; $name,\r\n            'email'   =&gt; $email,\r\n            'message' =&gt; $message\r\n        ];\r\n\r\n\r\n        $I-&gt;haveHttpHeader('Content-Type', 'application\/json');\r\n        $I-&gt;sendPOST(\r\n            '\/api\/messages',\r\n            json_encode($messagePost)\r\n        );\r\n\r\n        $I-&gt;seeResponseIsJson();\r\n\r\n        $response = json_decode($I-&gt;grabResponse(), true);\r\n\r\n        $I-&gt;assertEquals('messagesAction', $response);\r\n        $I-&gt;seeResponseCodeIs(200);\r\n        $I-&gt;seeNumRecords(1, 'message', $messagePost);\r\n    }\r\n\r\n    public function singleEmptyValueMessageStatus(ApiTester $I)\r\n    {\r\n        $time = microtime();\r\n\r\n        $name = 'test:' . $time;\r\n        $email = 'test@ify.com';\r\n        $message = 'This is a test:' . $time;\r\n\r\n        $messagePost = [\r\n            'name'    =&gt; $name,\r\n            'email'   =&gt; $email,\r\n            'message' =&gt; $message\r\n        ];\r\n\r\n        foreach ($messagePost as $key =&gt; $value) {\r\n            $postArray = $messagePost;\r\n            $postArray[$key] = '';\r\n\r\n            $I-&gt;haveHttpHeader('Content-Type', 'application\/json');\r\n            $I-&gt;sendPOST(\r\n                '\/api\/messages',\r\n                json_encode($postArray)\r\n            );\r\n\r\n            $I-&gt;seeResponseIsJson();\r\n\r\n            $response = json_decode($I-&gt;grabResponse(), true);\r\n\r\n            $I-&gt;assertContains('empty', $response);\r\n            $I-&gt;seeResponseCodeIs(400);\r\n            $I-&gt;dontSeeInDatabase('message', $postArray);\r\n        }\r\n    }\r\n\r\n    public function singleNotSetValueMessageStatus(ApiTester $I)\r\n    {\r\n        $time = microtime();\r\n\r\n        $name = 'test:' . $time;\r\n        $email = 'test@ify.com';\r\n        $message = 'This is a test:' . $time;\r\n\r\n        $messagePost = [\r\n            'name'    =&gt; $name,\r\n            'email'   =&gt; $email,\r\n            'message' =&gt; $message\r\n        ];\r\n\r\n        foreach ($messagePost as $key =&gt; $value) {\r\n            $postArray = $messagePost;\r\n            unset($postArray[$key]);\r\n\r\n            $I-&gt;haveHttpHeader('Content-Type', 'application\/json');\r\n            $I-&gt;sendPOST(\r\n                '\/api\/messages',\r\n                json_encode($postArray)\r\n            );\r\n\r\n            $I-&gt;seeResponseIsJson();\r\n\r\n            $response = json_decode($I-&gt;grabResponse(), true);\r\n\r\n            $I-&gt;assertContains('not set', $response);\r\n            $I-&gt;seeResponseCodeIs(401);\r\n            $I-&gt;dontSeeInDatabase('message', $postArray);\r\n        }\r\n    }\r\n}\r\n\r\n<\/code><\/pre>\n<div id=\"attachment_5386\" style=\"width: 310px\" class=\"wp-caption alignleft\"><a href=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-formular-test.jpg\" data-rel=\"penci-gallery-image-content\" ><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-5386\" class=\"size-medium wp-image-5386\" src=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-formular-test-300x300.jpg\" alt=\"Codeception Formular Test\" width=\"300\" height=\"300\" srcset=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-formular-test-300x300.jpg 300w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-formular-test-150x150.jpg 150w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-formular-test-768x770.jpg 768w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-formular-test-585x585.jpg 585w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-formular-test-640x640.jpg 640w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-formular-test.jpg 993w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-5386\" class=\"wp-caption-text\">Codeception Formular Test<\/p><\/div>\n<p>Die Geschwindigkeit ist hier nat\u00fcrlich deutlich h\u00f6her und eignet sich f\u00fcr die produktive Arbeit mit Tests. Hier kann man auch mit <a href=\"https:\/\/blog.nevercodealone.de\/symfony-live-coding\/\">TDD &#8211; Test Driven Development<\/a> arbeiten. Generell kann man festhalten, da\u00df Tests die nicht st\u00e4ndig ausgef\u00fchrt werden verfallen und Probleme machen. Hier ist es durchaus m\u00f6glich, gerade bei Frontend Tests, da\u00df sie gar keine richtigen Fehler finden, sondern nur angepasst werden m\u00fcssten. Deshalb verlieren diese Tests an Vertrauen. Denn es geht ja alles und trotzdem schl\u00e4gt der Test fehl. Und da komplexe Tests auch nicht so gerne von Kollegen gelesen und analysiert werden hat man hier leider sehr schnell ein Problem. Deshalb lieber kleine und gute Tests als riesige User Stories. Ich gehe hier einmal auf den ersten Test ein, da beide anderen Tests invalide Daten, den Error Code und ob es nicht in der Datenbank ankommt checken. Sie sind dem ersten Test als sehr \u00e4hnlich.<\/p>\n<h2>Microtime statt Time<\/h2>\n<div id=\"attachment_5379\" style=\"width: 310px\" class=\"wp-caption alignleft\"><a href=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-api-test.jpg\" data-rel=\"penci-gallery-image-content\" ><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-5379\" class=\"wp-image-5379 size-medium\" src=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-api-test-300x300.jpg\" alt=\"Codeception API Test\" width=\"300\" height=\"300\" srcset=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-api-test-300x300.jpg 300w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-api-test-150x150.jpg 150w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-api-test-768x770.jpg 768w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-api-test-585x585.jpg 585w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-api-test-640x640.jpg 640w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-api-test.jpg 993w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-5379\" class=\"wp-caption-text\">Codeception API Test<\/p><\/div>\n<p>Die Codeception Tests sind so schnell, da\u00df sie bei der Nutzung der time()-Methode f\u00fcr die \u00dcberpr\u00fcfung von Duplicate Content in der DB fehlschlagen w\u00fcrden. Es w\u00e4re dann in derselben Sekunde. Das mu\u00df man beachten, wenn man beispielsweise E-Mail Adressen mit einem Zeitstempel versieht.<\/p>\n<p>Hier sind die Input Daten im Array messagePost und werden so auch wieder in der Datenbank gesucht. Hier wird auch explizit nach nur einem Datensatz gesucht. Man kann ganz einfach Post, Get, Put etc. Requests gegen eine API Route schicken. Auf den Response kann man einige Assertions direkt anwenden. Hier brauche ich ein seeResponseIsJson und seeResponseCodeIs. F\u00fcr die Validierung des Response kann ich diesen in ein Array \u00fcbernehmen und mit den PHPUnit Assertions genauer verifizieren. Die werden als Modul in Codeception extra aktiviert und sind sehr hilfreich.<\/p>\n<h2>PHPUnit Test<\/h2>\n<pre><code class=\"php\">\r\nclass spamProtectionCest\r\n{\r\n    private $fixture;\r\n    private $I;\r\n\r\n    public function _before(UnitTester $I)\r\n    {\r\n        $this-&gt;I = $I;\r\n        $this-&gt;fixture = new SpamProtection();\r\n    }\r\n\r\n    \/\/ tests\r\n    public function validateUserInputsReturnTrueOnValidData(UnitTester $I)\r\n    {\r\n        $this-&gt;fixture = Stub::make(\r\n            $this-&gt;fixture,\r\n            [\r\n                'validateName' =&gt; true,\r\n                'validateEmail' =&gt; true,\r\n                'validateMessage' =&gt; true,\r\n                'validateIp' =&gt; true\r\n            ]\r\n        );\r\n\r\n        $data = [\r\n            'name' =&gt; '',\r\n            'email' =&gt; '',\r\n            'message' =&gt; '',\r\n            'ip' =&gt; ''\r\n        ];\r\n\r\n        $I-&gt;assertTrue($this-&gt;fixture-&gt;validateUserInputs($data));\r\n    }\r\n\r\n    public function validateNameEmptyIsFalse(UnitTester $I)\r\n    {\r\n        $methodReturn = $this-&gt;getMethodReturn('validateName', '');\r\n        $I-&gt;assertFalse($methodReturn);\r\n    }\r\n\r\n    public function validateNameIsSpam(UnitTester $I)\r\n    {\r\n        $methodReturn = $this-&gt;getMethodReturn('validateName', 'viagra');\r\n        $I-&gt;assertFalse($methodReturn);\r\n    }\r\n\r\n    \/**\r\n     * @dataProvider invalidIpsProvider\r\n     *\/\r\n    public function validateIpWithInvalidIpsReturnFalse(UnitTester $I, Example $data)\r\n    {\r\n        $ip = $data[0];\r\n        $methodReturn = $this-&gt;getMethodReturn('validateIp', $ip);\r\n        $I-&gt;assertFalse($methodReturn, 'IP: ' . $ip);\r\n    }\r\n    \/**\r\n     * @return array\r\n     *\/\r\n    protected function invalidIpsProvider() \/\/ alternatively, if you want the function to be public, be sure to prefix it with `_`\r\n    {\r\n        return [\r\n            [123],\r\n            ['192.168.0.1']\r\n        ];\r\n    }\r\n\r\n    \/**\r\n     * @dataProvider validIpsProvider\r\n     *\/\r\n    public function validateIpWithValidIpsReturnFalse(UnitTester $I, Example $data)\r\n    {\r\n        $this-&gt;fixture = Stub::make(\r\n            $this-&gt;fixture,\r\n            [\r\n                'isIpFromDe' =&gt; true\r\n            ]\r\n        );\r\n\r\n        $ip = $data[0];\r\n        $methodReturn = $this-&gt;getMethodReturn('validateIp', $ip);\r\n        $I-&gt;assertTrue($methodReturn, 'IP: ' . $ip);\r\n    }\r\n\r\n    \/**\r\n     * @return array\r\n     *\/\r\n    protected function validIpsProvider() \/\/ alternatively, if you want the function to be public, be sure to prefix it with `_`\r\n    {\r\n        return [\r\n            ['176.95.142.6'] \/\/ DE\r\n        ];\r\n    }\r\n\r\n    public function validateEmailEmptyStringReturnFalse(UnitTester $I)\r\n    {\r\n        $methodReturn = $this-&gt;getMethodReturn('validateEmail', '');\r\n        $I-&gt;assertFalse($methodReturn);\r\n    }\r\n\r\n    public function validateEmailNotValidEmailReturnFalse(UnitTester $I)\r\n    {\r\n        $methodReturn = $this-&gt;getMethodReturn('validateEmail', 'testify');\r\n        $I-&gt;assertFalse($methodReturn);\r\n    }\r\n\r\n    public function validateEmailValidEmailReturnTrue(UnitTester $I)\r\n    {\r\n        $methodReturn = $this-&gt;getMethodReturn('validateEmail', 'test@testify.com');\r\n        $I-&gt;assertTrue($methodReturn);\r\n    }\r\n\r\n    public function validateMessageEmptyReturnFalse(UnitTester $I)\r\n    {\r\n        $methodReturn = $this-&gt;getMethodReturn('validateMessage', '');\r\n        $I-&gt;assertFalse($methodReturn);\r\n    }\r\n\r\n    public function validateMessageSpamWordsReturnFalse(UnitTester $I)\r\n    {\r\n        $spamProtection = new SpamProtection();\r\n        $spamWords = $spamProtection-&gt;spamWords;\r\n\r\n        foreach ($spamWords as $spamWord) {\r\n            $methodReturn = $this-&gt;getMethodReturn('validateMessage', $spamWord);\r\n            $I-&gt;assertFalse($methodReturn, $spamWord);\r\n        }\r\n    }\r\n\r\n    public function validateMessageWithLowerUpperMixSpamWords(UnitTester $I)\r\n    {\r\n        $spamWords = [\r\n            'VIAGRA',\r\n            'ViAgRa',\r\n            'VIAgra'\r\n        ];\r\n\r\n        foreach ($spamWords as $spamWord) {\r\n            $methodReturn = $this-&gt;getMethodReturn('validateMessage', $spamWord);\r\n            $I-&gt;assertFalse($methodReturn, $spamWord);\r\n        }\r\n    }\r\n\r\n    \/**\r\n     * @throws \\ReflectionException\r\n     *\/\r\n    private function getMethodReturn($method, $param)\r\n    {\r\n        $class = new \\ReflectionClass($this-&gt;fixture);\r\n        $method = $class-&gt;getMethod($method);\r\n        $method-&gt;setAccessible(true);\r\n        $methodReturn = $method-&gt;invoke($this-&gt;fixture, $param);\r\n        return $methodReturn;\r\n    }\r\n\r\n\r\n\r\n}\r\n<\/code><\/pre>\n<h2>Schneller als sein Schatten und unglaublich m\u00e4chtig<\/h2>\n<div id=\"attachment_5387\" style=\"width: 287px\" class=\"wp-caption alignleft\"><a href=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-unit-test.jpg\" data-rel=\"penci-gallery-image-content\" ><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-5387\" class=\"size-medium wp-image-5387\" src=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-unit-test-277x300.jpg\" alt=\"Codeception Unit Test\" width=\"277\" height=\"300\" srcset=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-unit-test-277x300.jpg 277w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-unit-test-768x833.jpg 768w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-unit-test-585x634.jpg 585w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-unit-test.jpg 916w\" sizes=\"auto, (max-width: 277px) 100vw, 277px\" \/><\/a><p id=\"caption-attachment-5387\" class=\"wp-caption-text\">Codeception Unit Test<\/p><\/div>\n<p>Codeception PHPUnit Tests sind unglaublich schnell und brauchen \u00fcberhaupt keine vollst\u00e4ndige oder lauff\u00e4hige Umgebung. Sie k\u00f6nnen also in einem viel fr\u00fcheren Stadium eines Builds ausgef\u00fchrt werden. Dadurch haben sie einen weiteren starken Vorteil, weil sie viel schneller Feedback liefern k\u00f6nnen. Durch die Kapselung der Tests von allen anderen Units finden sie auch Bugs, die genau an der betreffenden Stelle sind. Denn am Ende testen sie schlie\u00dflich nur das genaue Unit und des Funktionalit\u00e4t. Hier wird ja der Test\u00a0validateUserInputsReturnTrueOnValidData ausgef\u00fchrt. Und der stellt nur sicher, da\u00df die Methode validateUserInputs true zur\u00fcck gibt, wenn alle Methoden zur Pr\u00fcfung gr\u00fcnes Licht geben. Es ist hier nicht relevant, ob die IP richtig ist. Denn daf\u00fcr ist eine andere Methode zust\u00e4ndig. Das ist am Anfang ein wenig gew\u00f6hnungsbed\u00fcrftig, hat aber am Ende viele zeitliche und inhaltliche Vorteile. Vor allem wird die Fehlersuche viel effizienter.<\/p>\n<h2>Codeception macht das Mocken so einfach<\/h2>\n<div id=\"attachment_5388\" style=\"width: 310px\" class=\"wp-caption alignleft\"><a href=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-html-report.png\" data-rel=\"penci-gallery-image-content\" ><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-5388\" class=\"wp-image-5388 size-medium\" src=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-html-report-300x172.png\" alt=\"Codeception HTML Report\" width=\"300\" height=\"172\" srcset=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-html-report-300x172.png 300w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-html-report-585x335.png 585w, https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-html-report.png 750w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-5388\" class=\"wp-caption-text\">Codeception HTML Report<\/p><\/div>\n<p>Das wichtigste bei der keder Applikation sind Features. Die werden bezahlt und die bringen uns weiter. Hier habe ich eine Formular Validierung und Spam Protection gebaut. Sicherlich bringt das Symfony Framework bereits viele sehr gute Validatoren mit, aber da ich die sehr individuell gestalten m\u00f6chte und auch Framework unabh\u00e4ngig einsetzen m\u00f6chte habe ich mich hier f\u00fcr eine eigene L\u00f6sung entschieden. Das ganze steht als Service in der Applikation zur Verf\u00fcgung und vielen Dank noch einmal an dieser Stelle f\u00fcr die Symfony 4 Entwickler und das neue Autowireing. Codeception Unit Tests testen immer nur exakt ein Unit und nicht die dar\u00fcber hinausgehende Abh\u00e4ngigkeit. Das bedeutet das Abh\u00e4ngigkeiten innerhalb der Methode gemockt werden. Es sind eben keine Functional- oder Integrationtests. Betrachten wir dazu einmal den ersten Test im Detail<\/p>\n<pre><code class=\"php\">\r\npublic function validateUserInputsReturnTrueOnValidData(UnitTester $I)\r\n    {\r\n        $this-&gt;fixture = Stub::make(\r\n            $this-&gt;fixture,\r\n            [\r\n                'validateName' =&gt; true,\r\n                'validateEmail' =&gt; true,\r\n                'validateMessage' =&gt; true,\r\n                'validateIp' =&gt; true\r\n            ]\r\n        );\r\n\r\n        $data = [\r\n            'name' =&gt; '',\r\n            'email' =&gt; '',\r\n            'message' =&gt; '',\r\n            'ip' =&gt; ''\r\n        ];\r\n\r\n        $I-&gt;assertTrue($this-&gt;fixture-&gt;validateUserInputs($data));\r\n    }\r\n\r\n<\/code><\/pre>\n<p>Obwohl ich ausschlie\u00dflich leere Werte als data-Array in die Methode validateUserInputs \u00fcbergebe kommt ein true zur\u00fcck. Mit der extrem einfachen Syntax von Codeception kann ich einfach ein Stub machen indem ich festlege was eine Methode von au\u00dferhalb zur\u00fcck gibt. Das kann ich auch mit anderen Klassen machen. Da alle Validator-Methoden jetzt true zur\u00fcckgeben macht das auch die validateUserInputs. Das bedeutet also im wesentlichen erstmal, da\u00df ich mir keine komplizierten Setups ausdenken mu\u00df, um z.B. zu schauen, ob eine IP jetzt aus Deutschland kam. Ich pr\u00fcfe hier ja nur, wenn alle Daten valide w\u00e4ren, da\u00df true zur\u00fcck kommt. Denn das ist alles was die Methode tut. Genauso k\u00f6nnte ich auch einen Test schreiben, der immer eine Methode auf false setzt und dann auf false validiert.<\/p>\n<pre><code class=\"php\">\r\nProtected Methoden testen und Data Provider\r\n\r\n\/**\r\n     * @throws \\ReflectionException\r\n     *\/\r\n    private function getMethodReturn($method, $param)\r\n    {\r\n        $class = new \\ReflectionClass($this-&gt;fixture);\r\n        $method = $class-&gt;getMethod($method);\r\n        $method-&gt;setAccessible(true);\r\n        $methodReturn = $method-&gt;invoke($this-&gt;fixture, $param);\r\n        return $methodReturn;\r\n    }\r\n\r\n\/**\r\n     * @dataProvider invalidIpsProvider\r\n     *\/\r\n    public function validateIpWithInvalidIpsReturnFalse(UnitTester $I, Example $data)\r\n    {\r\n        $ip = $data[0];\r\n        $methodReturn = $this-&gt;getMethodReturn('validateIp', $ip);\r\n        $I-&gt;assertFalse($methodReturn, 'IP: ' . $ip);\r\n    }\r\n\r\n    \/**\r\n     * @return array\r\n     *\/\r\n    protected function invalidIpsProvider()    {\r\n        return [\r\n            [123],\r\n            ['192.168.0.1']\r\n        ];\r\n    }\r\n<\/code><\/pre>\n<p>Sollen Methoden die nicht public sind getestet werden. Ich finde ja, weil sie jeweils separate Features f\u00fcr die Applikation liefern und ich so viel genauer die richtige Stelle testen kann. Deshalb habe ich mir eine Methode gebaut, die Methoden mit Hilfe einer ReflectionClass public setzen kann und den Return zur\u00fcck gibt. Betrachten wir dazu die validateIpWithInvalidIpsReturnFalse Methode. Hier werden \u00fcber einen Data Provider zwei IPs \u00fcbergeben und nacheinander getestet. Gerade bei mehreren zu \u00fcberpr\u00fcfenden Werten ist das sehr praktisch.<\/p>\n<h2>Fazit zum Thema Codeception Testing<\/h2>\n<p>Codeception Tests sind ein Werkzeug, da\u00df jeder Entwickler der es kennt zu sch\u00e4tzen wei\u00df und ihm viel Zeit bringt. Wichtige Zeit, die f\u00fcr Refactoring und Verbesserungen verwendet werden kann. Dadurch ist es m\u00f6glich sehr gute Software zu produzieren. Ohne Tests ist das absolut nicht m\u00f6glich, weil man im Legacy Code steckt und sich auch immer wieder um manuelle Deployments und Basis Tests k\u00fcmmern mu\u00df. Weil so aber keine wirklichen Bugs gefunden werden steigt die Change-Fail-Rate. Also immer wenn man was \u00e4ndert fliegt was anderes weg. Als Entwickler ger\u00e4t man so stark unter Druck von allen Seiten, da man in die alleinige und volle Verantwortung genommen wird ein stabiles Kartenhaus auszuliefern. Das macht auf Dauer weich und krank. Krankheitsbilder wie Anpassungsschwierigkeiten oder gar Burnouts sind langwierig und wirklich gef\u00e4hrlich. Das ist nicht zu untersch\u00e4tzen.<\/p>\n<h2>Wir helfen gerne<\/h2>\n<p>Alle Projekte und unser gesamtes <a href=\"https:\/\/presentations.entwicklungshilfe.nrw\/#\/\">Schulungs und Pr\u00e4sentationsmaterial<\/a> ist \u00f6ffentlich als Open Source auf den Github Repositories von <a href=\"https:\/\/github.com\/nevercodealone\" target=\"_blank\" rel=\"noopener noreferrer\">Never Code Alone<\/a> und der <a href=\"https:\/\/github.com\/Entwicklungshilfe-NRW\" target=\"_blank\" rel=\"noopener noreferrer\">Entwicklungshilfe NRW<\/a> verf\u00fcgbar. Hier gibt es auch den gesamten Quellcode von den Beispielen hier oben auf <a href=\"https:\/\/github.com\/nevercodealone\/nevercodealone\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/github.com\/nevercodealone\/nevercodealone<\/a>. Unsere Pr\u00e4sentationen halten wir gerne auf Meetups, Usergroups und Firmen und auf Konferenzen. Sprecht uns daf\u00fcr einfach an.<\/p>\n<h2>Social Media<\/h2>\n<p>Dazu sind wir auf drei Social Media Kan\u00e4len aktiv. Auf Facebook gibt es regelm\u00e4\u00dfig die #NCADiscussion. Hier werden Arbeitsbedingungen von Entwicklern diskutiert und versucht sich \u00fcber Probleme auszutauschen. Auf Instagram gibt es das Developer Tagebuch von mir selbst und auf Twitter hilfreiche Links f\u00fcr die Entwicklung und Retweets von wichtigen Tweets. Folgen lohnt sich<\/p>\n<p>Roland Golla ist Speaker und Trainer bei der Entwicklungshilfe NRW und bringt mit Never Code Alone gute Arbeitgeber mit guten Web Developern zusammen.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Gute Codeception Tests finden Bugs und sparen ein Drittel der Entwicklungszeit. Aber es gibt viele&hellip;<\/p>\n","protected":false},"author":1,"featured_media":5376,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_crdt_document":"","_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"cybocfi_hide_featured_image":"","footnotes":""},"categories":[4,7,8,12],"tags":[18,32,33,34,37,40,42,46],"class_list":["post-5364","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-gastbeitrag","category-php-framework","category-software-qualitaet","category-webdevelopment-tools","tag-code","tag-php","tag-php-schulung","tag-phpstorm","tag-software-qualitaet","tag-tests","tag-tutorial","tag-webdevelopment"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.0 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Codeception Tutorial - Automatisierte Tests f\u00fcr ein besseres Leben<\/title>\n<meta name=\"description\" content=\"Codeception Tutorial und HowTo f\u00fcr automatisierte Tests als Acceptance, API und PHOUnit Tests an einem praktischen Symfony 4 Beispiel\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blog.nevercodealone.de\/codeception-tutorial\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Codeception Tutorial - Automatisierte Tests f\u00fcr ein besseres Leben\" \/>\n<meta property=\"og:description\" content=\"Codeception Tutorial und HowTo f\u00fcr automatisierte Tests als Acceptance, API und PHOUnit Tests an einem praktischen Symfony 4 Beispiel\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blog.nevercodealone.de\/codeception-tutorial\/\" \/>\n<meta property=\"og:site_name\" content=\"Employer Branding und Tutorials Web Development\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/nevercodealone\/\" \/>\n<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/roland.golla\" \/>\n<meta property=\"article:published_time\" content=\"2018-07-25T09:09:21+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2019-04-24T12:54:35+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-tutorial-howto.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1000\" \/>\n\t<meta property=\"og:image:height\" content=\"500\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Roland Golla\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-tutorial-howto.jpg\" \/>\n<meta name=\"twitter:creator\" content=\"@https:\/\/twitter.com\/cleancode_devel\" \/>\n<meta name=\"twitter:site\" content=\"@nevercodealone\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Roland Golla\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"19\u00a0Minuten\" \/>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Codeception Tutorial - Automatisierte Tests f\u00fcr ein besseres Leben","description":"Codeception Tutorial und HowTo f\u00fcr automatisierte Tests als Acceptance, API und PHOUnit Tests an einem praktischen Symfony 4 Beispiel","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:\/\/blog.nevercodealone.de\/codeception-tutorial\/","og_locale":"de_DE","og_type":"article","og_title":"Codeception Tutorial - Automatisierte Tests f\u00fcr ein besseres Leben","og_description":"Codeception Tutorial und HowTo f\u00fcr automatisierte Tests als Acceptance, API und PHOUnit Tests an einem praktischen Symfony 4 Beispiel","og_url":"https:\/\/blog.nevercodealone.de\/codeception-tutorial\/","og_site_name":"Employer Branding und Tutorials Web Development","article_publisher":"https:\/\/www.facebook.com\/nevercodealone\/","article_author":"https:\/\/www.facebook.com\/roland.golla","article_published_time":"2018-07-25T09:09:21+00:00","article_modified_time":"2019-04-24T12:54:35+00:00","og_image":[{"width":1000,"height":500,"url":"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-tutorial-howto.jpg","type":"image\/jpeg"}],"author":"Roland Golla","twitter_card":"summary_large_image","twitter_image":"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-tutorial-howto.jpg","twitter_creator":"@https:\/\/twitter.com\/cleancode_devel","twitter_site":"@nevercodealone","twitter_misc":{"Verfasst von":"Roland Golla","Gesch\u00e4tzte Lesezeit":"19\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blog.nevercodealone.de\/codeception-tutorial\/#article","isPartOf":{"@id":"https:\/\/blog.nevercodealone.de\/codeception-tutorial\/"},"author":{"name":"Roland Golla","@id":"https:\/\/blog.nevercodealone.de\/#\/schema\/person\/abb634fbea84896d032351009372debb"},"headline":"Codeception Tutorial &#8211; Automatisierte Tests f\u00fcr ein besseres Leben","datePublished":"2018-07-25T09:09:21+00:00","dateModified":"2019-04-24T12:54:35+00:00","mainEntityOfPage":{"@id":"https:\/\/blog.nevercodealone.de\/codeception-tutorial\/"},"wordCount":2886,"commentCount":10,"publisher":{"@id":"https:\/\/blog.nevercodealone.de\/#organization"},"image":{"@id":"https:\/\/blog.nevercodealone.de\/codeception-tutorial\/#primaryimage"},"thumbnailUrl":"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-tutorial-howto.jpg","keywords":["Code","PHP","PHP-Schulung","PhpStorm","Software-Qualit\u00e4t","Tests","Tutorial","Webdevelopment"],"articleSection":["Gastbeitrag","PHP-Framework","Software-Qualit\u00e4t","Webdevelopment-Tools"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/blog.nevercodealone.de\/codeception-tutorial\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/blog.nevercodealone.de\/codeception-tutorial\/","url":"https:\/\/blog.nevercodealone.de\/codeception-tutorial\/","name":"Codeception Tutorial - Automatisierte Tests f\u00fcr ein besseres Leben","isPartOf":{"@id":"https:\/\/blog.nevercodealone.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/blog.nevercodealone.de\/codeception-tutorial\/#primaryimage"},"image":{"@id":"https:\/\/blog.nevercodealone.de\/codeception-tutorial\/#primaryimage"},"thumbnailUrl":"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-tutorial-howto.jpg","datePublished":"2018-07-25T09:09:21+00:00","dateModified":"2019-04-24T12:54:35+00:00","description":"Codeception Tutorial und HowTo f\u00fcr automatisierte Tests als Acceptance, API und PHOUnit Tests an einem praktischen Symfony 4 Beispiel","breadcrumb":{"@id":"https:\/\/blog.nevercodealone.de\/codeception-tutorial\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.nevercodealone.de\/codeception-tutorial\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/blog.nevercodealone.de\/codeception-tutorial\/#primaryimage","url":"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-tutorial-howto.jpg","contentUrl":"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2018\/07\/codeception-tutorial-howto.jpg","width":1000,"height":500,"caption":"Codeception Tutorial HowTo"},{"@type":"BreadcrumbList","@id":"https:\/\/blog.nevercodealone.de\/codeception-tutorial\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Startseite","item":"https:\/\/blog.nevercodealone.de\/"},{"@type":"ListItem","position":2,"name":"Codeception Tutorial &#8211; Automatisierte Tests f\u00fcr ein besseres Leben"}]},{"@type":"WebSite","@id":"https:\/\/blog.nevercodealone.de\/#website","url":"https:\/\/blog.nevercodealone.de\/","name":"Employer Branding und PHP Training","description":"Employer Branding f\u00fcr Web Development Jobs, viele Tutorials und HowTo Gastbeitr\u00e4ge der Community","publisher":{"@id":"https:\/\/blog.nevercodealone.de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blog.nevercodealone.de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/blog.nevercodealone.de\/#organization","name":"Never Code Alone","url":"https:\/\/blog.nevercodealone.de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/blog.nevercodealone.de\/#\/schema\/logo\/image\/","url":"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2016\/03\/nca_2.png","contentUrl":"https:\/\/blog.nevercodealone.de\/wp-content\/uploads\/2016\/03\/nca_2.png","width":212,"height":130,"caption":"Never Code Alone"},"image":{"@id":"https:\/\/blog.nevercodealone.de\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/nevercodealone\/","https:\/\/x.com\/nevercodealone","https:\/\/www.instagram.com\/nevercodealone\/","https:\/\/www.youtube.com\/c\/NeverCodeAlone"]},{"@type":"Person","@id":"https:\/\/blog.nevercodealone.de\/#\/schema\/person\/abb634fbea84896d032351009372debb","name":"Roland Golla","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/blog.nevercodealone.de\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/f142b13f7d2f172e4d961904f43cfbbee9cdb799ea5325fe8dbf25f3cc9767f2?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f142b13f7d2f172e4d961904f43cfbbee9cdb799ea5325fe8dbf25f3cc9767f2?s=96&d=mm&r=g","caption":"Roland Golla"},"description":"Initiator von Never Code Alone, PHP-Trainer und Consultant f\u00fcr Softwarequalit\u00e4t rolandgolla.de - Das PHP-Training gibt es hier","sameAs":["http:\/\/www.rolandgolla.de","https:\/\/www.facebook.com\/roland.golla","https:\/\/www.linkedin.com\/in\/rolandgolla","https:\/\/x.com\/https:\/\/twitter.com\/cleancode_devel"],"url":"https:\/\/blog.nevercodealone.de\/author\/rolandgolla\/"}]}},"_links":{"self":[{"href":"https:\/\/blog.nevercodealone.de\/wp-json\/wp\/v2\/posts\/5364","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.nevercodealone.de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.nevercodealone.de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.nevercodealone.de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.nevercodealone.de\/wp-json\/wp\/v2\/comments?post=5364"}],"version-history":[{"count":21,"href":"https:\/\/blog.nevercodealone.de\/wp-json\/wp\/v2\/posts\/5364\/revisions"}],"predecessor-version":[{"id":5998,"href":"https:\/\/blog.nevercodealone.de\/wp-json\/wp\/v2\/posts\/5364\/revisions\/5998"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.nevercodealone.de\/wp-json\/wp\/v2\/media\/5376"}],"wp:attachment":[{"href":"https:\/\/blog.nevercodealone.de\/wp-json\/wp\/v2\/media?parent=5364"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.nevercodealone.de\/wp-json\/wp\/v2\/categories?post=5364"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.nevercodealone.de\/wp-json\/wp\/v2\/tags?post=5364"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}