Starten Sie Ihre PHP-Tests mit Codeception
Veröffentlicht: 2022-03-11Bevor wir zu Codeception und PHP übergehen, sollten wir die Grundlagen behandeln und zunächst erklären, warum wir überhaupt Tests in Anwendungen benötigen. Vielleicht könnten wir zumindest dieses Mal ein Projekt abschließen, ohne Zeit mit Tests zu verschwenden?
Sicher, man braucht nicht für alles Tests; zum Beispiel, wenn Sie noch eine weitere Homepage bauen möchten. Wahrscheinlich brauchen Sie keine Tests, wenn Ihr Projekt statische Seiten enthält, die von einem Router verbunden sind.
Sie müssen jedoch definitiv getestet werden, wenn:
- Ihr Team verwendet BDD/TDD.
- Ihr Git-Repository enthält mehr als ein paar Commits.
- Sie sind ein richtiger Profi und arbeiten an einem ernsthaften Projekt.
Sie können sich entschuldigen, indem Sie sagen, dass Sie bereits eine eigene Testabteilung haben, eine Gruppe von Leuten, die Tests durchführen und bei Bedarf neue schreiben. Aber können Sie sich vorstellen, wie lange die Fehlerbehebung dauern wird, nachdem Sie Ihrem Projekt neue Funktionen hinzugefügt haben?
Was löst Testen?
Lassen Sie uns zunächst entscheiden, welche Art von Problemen durch Tests gelöst werden können. Sie können nicht alle Ihre Fehler mit Testen beseitigen, aber Sie können das erwartete Verhalten in Testfällen beschreiben. Die Fehler könnten sich in Ihren Testfällen befinden. Spaghetti-Code bleibt Spaghetti-Code, auch wenn Sie Testfälle verwenden.
Sie können jedoch sicher sein, dass Ihr Code später geändert wird (durch Beheben von Fehlern oder Hinzufügen neuer Funktionen), sodass Ihr Code immer noch frei von Fehlern ist, die im Test beschrieben wurden. Außerdem können auch gut geschriebene Tests manchmal in der Dokumentation verwendet werden, weil man dort sehen kann, wie sich typische Szenarien entwickeln und das erwartete Verhalten überprüfen. Wir können sagen, dass Testen eine kleine, aber entscheidende Investition in die Zukunft ist.
Welche Art von Tests können wir also anwenden?
- Komponententests: Low-Level-Tests, die kleine Teile Ihres Codes überprüfen – die Methoden Ihrer Klasse isoliert von anderem Code.
- Integrationstests: Integrationstests prüfen einen Teil Ihrer Anwendung, sie können mehrere Klassen oder Methoden enthalten, sollten sich aber auf ein Feature beschränken. Dieser Test sollte auch überprüfen, wie verschiedene Klassen interagieren.
- Funktionstests: Testet spezifische Anfragen an Ihre Anwendung: Browserantwort, Datenbankänderungen und so weiter.
- Abnahmetest: In den meisten Fällen bedeutet Abnahmetest, ob die Anwendung alle Kundenanforderungen erfüllt.
Nehmen wir zur Verdeutlichung an, wir veranschaulichen den Prozess mit etwas Greifbarem, z. B. einem Gebäude. Ein Gebäude besteht aus kleinen Blöcken, die Wände bilden. Jeder Ziegel muss bestimmte Anforderungen erfüllen; es muss der erforderlichen Belastung standhalten, ein bestimmtes Volumen und eine bestimmte Form haben und so weiter. Das sind Unit-Tests. Die Idee von Integrationstests besteht darin, zu überprüfen, wie fest und genau die Ziegel aneinander haften, wie sie in ein bestimmtes Element des Gebäudes integriert sind. Funktionstests können mit Tests an einer einzelnen Gebäudewand verglichen werden, um zu prüfen, ob der Innenraum vor Witterungseinflüssen geschützt ist und ob die Sonne durch das Fenster zu sehen ist oder nicht. Bei der Abnahmeprüfung wird das gesamte Gebäude als Gesamtprodukt getestet: Tür öffnen, hineingehen, Tür schließen, Licht einschalten, in den zweiten Stock steigen und einen Blick in den Garten vor dem Gebäude werfen.
Lernen Sie Codeception kennen
Diese Unterteilung ist jedoch bedingt und manchmal ist es schwierig, der Versuchung zu widerstehen, verschiedene Arten von Tests zu mischen.
Viele Entwickler verwenden Unit-Tests und behaupten, das sei genug. Ich war früher ein solcher Entwickler; Ich fand die Verwendung verschiedener Systeme für verschiedene Arten von Tests zu schwierig und zeitaufwändig. Vor einiger Zeit habe ich mich entschieden, etwas Nützlicheres als PHPUnit zu finden; Ich wollte meinen Code besser testen, aber ich wollte nicht Unmengen an Dokumentation lesen und lernen und nach Fallstricken suchen. So habe ich Codeception entdeckt. Zuerst war ich skeptisch, wie wir es oft sind, wenn es um etwas Neues geht (dieses Projekt ist fünf Jahre alt, also technisch gesehen kann es nicht als „neu“ bezeichnet werden), aber nachdem ich ein paar Tage damit herumgespielt hatte , kam ich zu dem Schluss, dass Codeception ein sehr nützliches und leistungsstarkes System ist.
Wie installiert man also Codeception? Es ist so einfach wie es geht:
$ composer require "codeception/codeception" $ php vendor/bin/codecept bootstrap
Nach der Installation finden Sie in Ihrem Projekt einen neuen Ordner mit dem Namen tests , und es gibt einige Unterordner mit den Namen Acceptance, Functional und Unit . Es sieht so aus, als könnten wir mit dem Schreiben unserer Tests beginnen. Cool, aber was kommt als nächstes?
Versuchen Sie nun, einen Standard-Hello-World- Akzeptanztest hinzuzufügen.
$ php vendor/bin/codecept generate:cept acceptance HelloWorld
Nun erhalten wir eine Akzeptanztestdatei tests/acceptance/HelloWorldCept.php mit folgendem Inhalt:
<?php $I = new AcceptanceTester($scenario); $I->wantTo('perform actions and see result');
Die Standardvariable mit dem Namen $I
ist nicht nur ein Buchstabe; es ist ein Charakter. Was führt die Tests durch? Der Tester natürlich. Dieser Tester öffnet die Seite oder Klasse Ihrer Website, macht etwas damit und zeigt Ihnen das Endergebnis seiner Aktionen. Sie werden sehen, was funktioniert hat und was schief gelaufen ist. Deshalb heißt dieses Objekt $I
und enthält Methoden namens wantTo()
, see()
oder amOnPage()
.
Denken wir also wie ein Tester über Möglichkeiten, die Funktionsfähigkeit einer Seite zu überprüfen. Der erste Ansatz besteht darin, die Seite zu öffnen und nach einer Phrase zu suchen. Es beweist, dass die Seite für Besucher verfügbar ist.
Das sollte einfach sein:
<?php $I->amOnPage('/'); $I->see('Welcome');
Wir können diesen Befehl verwenden, um die Tests von Codeception auszuführen:
$ php vendor/bin/codecept run
Wir sehen sofort, dass etwas nicht stimmt. Auf den ersten Blick scheint die Botschaft zu lang und unklar zu sein, aber wenn wir genauer hinschauen, wird alles offensichtlich.
Wir hatten einen Test, Acceptance , und es wurde ein Fehler festgestellt:
Acceptance Tests (1) Perform actions and see result (HelloWorldCept) Error ---------- 1) Failed to perform actions and see result in HelloWorldCept (tests/acceptance/HelloWorldCept .php) [GuzzleHttp\Exception\ConnectException] cURL error 6: Could not resolve host: localhost (see http://curl.haxx.se/libcurl/c/libcurl-errors.html)
Das ist der Übeltäter: localhost ist nicht verfügbar.
Und hier sind die Szenarioschritte unseres Tests:
1. $I->amOnPage("/")
Ok, öffnen wir tests/acceptance.suite.yml und ändern die url: http://localhost/
in etwas, das tatsächlich verfügbar ist. In meinem Fall ist es mein lokaler Testhost, url: https://local.codeception-article.com/
Führen Sie den Test erneut aus und Sie sollten Folgendes erhalten:
Acceptance Tests (1) --------------------------------------------------------------------------------------- Perform actions and result (HelloWorldCept) Ok
Hurra! Unser erster erfolgreicher Test!
Natürlich ist amOnPage()
nicht die einzige verfügbare Testmethode, wir haben sie lediglich für unser Beispiel herausgegriffen. Alle Codeception-Testmethoden lassen sich in folgende Gruppen einteilen:
- Interaktion mit Seite:
fillField()
,selectOption()
,submitForm()
,click()
- Behauptungen.
see()
,dontSee()
,seeElement()
,seeInCurrentUrl()
,seeCheckboxIsChecked()
,seeInField()
,seeLink()
. All diesen Methoden können Sie ein Suffix hinzufügen und es verwenden, wenn Sie eine Methode benötigen, die das Testszenario nicht unterbricht, wenn etwas nicht gefunden werden kann. - Cookie-Methoden:
setCookie()
,grabCookie()
,seeCookie()
- Kommentar und Beschreibung der Testszenarien:
amGoingTo()
,wantTo()
) , Expect(expect()
. Verwenden Sie diese Methoden, um gut kommentierte und beschriebene Tests zu erhalten, die Ihnen helfen, sich an die Ziele des Tests zu erinnern.
Wenn wir also die E-Mail-Seite zum Zurücksetzen des Passworts testen würden, könnten wir dies folgendermaßen tun:
<?php $I = new AcceptanceTester($scenario); $I->wantTo('Test forgotten password functionality'); $I->amOnPage('/forgotten') $I->see('Enter email'); $I->fillField('email', '[email protected]'); $I->click('Continue'); $I->expect('Reset password link not sent for incorrect email'); $I->see('Email is incorrect, try again'); $I->amGoingTo('Fill correct email and get link'); $I->see('Enter email'); $I->fillField('email', '[email protected]'); $I->click('Continue'); $I->expect('Reset password link sent for correct email'); $I->see('Please check your email for next instructions');
Es sieht so aus, als ob dies funktionieren sollte, aber was ist, wenn es einige Ajax-geladene Teile auf der Seite gibt? Können wir so eine Seite testen? Die Antwort ist, dass Codeception standardmäßig PhpBrowser verwendet, das auf Symfony BrowserKit und Guzzle basiert. Es ist einfach, schnell und Sie müssen nur kräuseln, um es zu verwenden.

Sie könnten Selenium auch verwenden und Seiten mit echten Browsern testen. Ja, es wird langsamer, aber Sie können auch JavaScript testen.
Zuerst müssen Sie den Selenium-Treiber installieren, die Acceptance.suite.yml ändern und die AcceptanceTester-Klasse neu erstellen. Danach können Sie die Methoden wait()
und waitForElement()
. Und was noch interessanter ist, Sie können Zeit und Ressourcen sparen, indem Sie die Methoden saveSessionSnapshot()
und loadSessionSnapshot()
. Mit dieser Methode können Sie den Sitzungsstatus speichern und neue Tests in früheren Sitzungen starten. Dies ist in einigen Situationen nützlich, beispielsweise im Testautorisierungsprozess.
So haben wir am Ende eine einfache, aber leistungsstarke Möglichkeit, viele Funktionen zu testen.
Funktionsprüfung
OK, Zeit, mit dem Funktionstest fortzufahren.
$ php vendor/bin/codecept generate:cept functional HelloWorld
Und das bekommen wir:
<?php $I = new FunctionalTester($scenario); $I->amOnPage('/'); $I->see('Welcome');
Warte was?
Nein, es ist kein Fehler. Funktionale Tests sollten genauso geschrieben werden wie Integrationstests. Der Unterschied besteht darin, dass Funktionstests direkt mit Ihrer Anwendung interagieren. Das bedeutet, dass Sie keinen Webserver zum Ausführen von Funktionstests benötigen und mehr Kapazität zum Testen verschiedener Teile Ihrer Anwendung haben.
Es bedeutet zwar, dass die Unterstützung für alle Frameworks fehlt, aber die Liste der unterstützten Frameworks ist umfangreich: Symfony, Silex, Phalcon, Yii, Zend Framework, Lumen, Laravel. Dies sollte für die meisten Fälle und die meisten Entwickler ausreichen. Bitte konsultieren Sie die Moduldokumentation von Codeception, um eine Liste der verfügbaren Funktionen zu erhalten, und schalten Sie sie dann einfach in der functional.suite.yml
ein.
Bevor wir mit den Komponententests fortfahren, gestatten Sie mir einen kleinen Exkurs. Wie Sie vielleicht bemerkt haben, haben wir unsere Tests mit keycept erstellt:
$ php vendor/bin/codecept generate: cept acceptance HelloWorld
Dies ist nicht die einzige Möglichkeit, Tests zu erstellen. Es gibt auch Cest-Tests . Der Unterschied besteht darin, dass Sie mehrere verwandte Szenarien in einer Klasse strukturieren können:
$ php vendor/bin/codecept generate:cest acceptance HelloWorld <?php class HelloWorldCest { public function _before(AcceptanceTester $I) { $I->amOnPage('/forgotten') } public function _after(AcceptanceTester $I) { } // tests public function testEmailField(AcceptanceTester $I) { $I->see('Enter email'); } public function testIncorrectEmail(AcceptanceTester $I) { $I->fillField('email', '[email protected]'); $I->click('Continue'); $I->see('Email is incorrect, try again'); } public function testCorrectEmail(AcceptanceTester $I) { $I->fillField('email', '[email protected]'); $I->click('Continue'); $I->see('Please check your email for next instructions'); } }
In diesem Beispiel werden die Methoden _before()
und _after()
vor und nach jedem Test ausgeführt. Jedem Test wird eine Instanz der AcceptanceTester
-Klasse übergeben, sodass Sie sie auf die gleiche Weise wie in Cest-Tests verwenden können. Diese Art von Tests kann in bestimmten Situationen nützlich sein, es lohnt sich also, sie im Hinterkopf zu behalten.
Unit-Tests
Zeit für einige Unit-Tests.
Codeception basiert auf PHPUnit, sodass Sie für PHPUnit geschriebene Tests verwenden können. Um neue PHPUnit-Tests hinzuzufügen, verwenden Sie den folgenden Ansatz:
$ php vendor/bin/codecept generate:phpunit unit HelloWorld
Oder erben Sie einfach Ihre Tests auf \PHPUnit_Framework_TestCase
.
Aber wenn Sie etwas mehr wollen, sollten Sie die Unit-Tests von Codeception ausprobieren:
$ php vendor/bin/codecept generate:test unit HelloWorld <?php class HelloWorldTest extends \Codeception\TestCase\Test { /** * @var \UnitTester */ protected $tester; protected function _before() { } protected function _after() { } // tests public function testUserSave() { $user = User::find(1); $user->setEmail('[email protected]'); $user->save(); $user = User::find(1); $this->assertEquals('[email protected]', $user->getEmail()); } }
Vorerst nichts Ungewöhnliches. Die Methoden _before()
und _after()
sind analog zu setUp()
und tearDown()
und werden vor und nach jedem Test ausgeführt.
Der Hauptvorteil dieses Tests liegt in seiner Fähigkeit, Ihren Testprozess zu erweitern, indem Module hinzugefügt werden, die in unit.suite.yml
werden können:
- Zugriff auf Memcache und Datenbanken zum Verfolgen von Änderungen (MySQL, SQLite, PostgreSQL, MongoDB werden unterstützt)
- Testen von REST/SOAP-Anwendungen
- Warteschlangen
Jedes Modul hat seine eigenen Funktionen, daher ist es am besten, die Dokumentation zu überprüfen und die notwendigen Informationen für jedes Modul zu sammeln, bevor Sie mit den eigentlichen Tests fortfahren.
Außerdem können Sie das Codeception/Specify-Paket verwenden (das zu composer.json
hinzugefügt werden muss) und eine Beschreibung wie folgt schreiben:
<?php class HelloWorldTest extends \Codeception\TestCase\Test { use \Codeception\Specify; private $user; protected function _before() { $this->user = User::find(1); } public function testUserEmailSave() { $this->specify("email can be stored", function() { $this->user->setEmail('[email protected]'); $this->user->save(); $user = User::find(1); $this->assertEquals('[email protected]', $user->getEmail()); }); } }
Der PHP-Code innerhalb dieser Abschlussfunktionen ist isoliert, sodass sich Änderungen darin nicht auf den Rest Ihres Codes auswirken. Beschreibungen helfen Ihnen dabei, den Test lesbarer zu machen und es einfacher zu machen, fehlgeschlagene Tests zu identifizieren.
Als optionales Extra können Sie das Paket Codeception\Verify
für eine BDD-ähnliche Syntax verwenden:
<?php public function testUserEmailSave() { verify($map->getEmail())->equals('[email protected]'); }
Und natürlich können Sie Stubs verwenden:
<?php public function testUserEmailSave() { $user = Stub::make('User', ['getEmail' => '[email protected]']); $this->assertEquals('[email protected]', $user->getEmail()); }
Fazit: Codeception spart Zeit und Aufwand
Was können Sie also von Codeception erwarten? Für wen ist es? Gibt es Vorbehalte?
Meiner Meinung nach eignet sich dieses Testing-Framework für die unterschiedlichsten Teams: Große und kleine, Anfänger und kampferprobte PHP-Profis, solche, die ein beliebtes Framework verwenden, und solche, die kein Framework verwenden.
In jedem Fall läuft alles darauf hinaus: Codeception ist bereit für die Hauptsendezeit.
Es ist ein ausgereiftes und gut dokumentiertes Framework, das leicht um zahlreiche Module erweitert werden kann. Codeception ist modern, basiert aber auf dem bewährten PHPUnit, was Entwickler beruhigen sollte, die nicht zu viel experimentieren wollen.
Es funktioniert gut, was bedeutet, dass es schnell ist und nicht zu viel Zeit und Mühe erfordert. Besser noch, es ist relativ einfach zu beherrschen, und eine umfangreiche Dokumentation sollte einen problemlosen Lernprozess unterstützen.
Codeception ist auch einfach zu installieren und zu konfigurieren, bietet jedoch viele erweiterte Optionen. Während die meisten Benutzer nicht alle (oder tatsächlich die meisten) davon benötigen, hängt alles davon ab, was Sie damit beabsichtigen. Sie können mit den Grundlagen beginnen, und die zusätzlichen Funktionen werden sich früher oder später als nützlich erweisen.