Test je PHP Codebase met EnhancePHP

Je weet het; Ik weet het. We zouden onze code meer moeten testen dan wij. Een deel van de reden waarom we dat niet doen, denk ik, is dat we niet precies weten hoe. Nou, ik ga vandaag van dat excuus af: ik leer je om je PHP te testen met het EnhancePHP-framework.


Maak kennis met EnhancePHP

Ik ga niet proberen je te overtuigen om je code te testen; en we gaan ook niet over Test Driven Development praten. Dat is eerder gedaan op Nettuts +. In dat artikel legt Nikko Bautista precies uit waarom testen een goede zaak is en schetst een TDD-workflow. Lees dat eens, als je niet bekend bent met TDD. Hij gebruikt ook de SimpleTest-bibliotheek voor zijn voorbeelden, dus als je het uiterlijk van EnhancePHP niet leuk vindt, kun je SimpleTest als alternatief proberen.

Zoals ik al zei, gebruiken we de EnhancePHP. Het is een geweldige kleine PHP-bibliotheek (één bestand) die veel testfunctionaliteit biedt.

Begin door naar hun downloadpagina te gaan en de nieuwste versie van het framework te pakken.

We gaan een heel eenvoudige Validation-klasse bouwen om te testen. Het zal niet teveel doen: gewoon terugkeren waar als het item de validatie passeert, of vals als dat niet het geval is. Dus, maak een heel eenvoudig klein project:

We doen dit is een semi-TDD-mode, dus laten we beginnen met het schrijven van een paar tests.


Tests schrijven

Out little class gaat drie dingen valideren: e-mailadressen, gebruikersnamen en telefoonnummers.

Maar voordat we echte tests gaan schrijven, moeten we onze klas instellen:

  val = nieuwe validatie (); 

Dit is onze start; merk op dat we de klas uitbreiden \ Verbeteren \ TestFixture. Door dit te doen laten we EnhancePHP weten dat openbare methoden in deze klasse tests zijn, met uitzondering van methoden opstelling en scheuren. Zoals u wellicht vermoedt, lopen deze methoden voor en na al uw tests (niet voor en na elke test). In dit geval onze opstelling methode zal een nieuw maken bevestiging bijvoorbeeld en wijs het toe aan een eigenschap op ons exemplaar.

Trouwens, als je nog relatief onbekend bent met PHP, ben je daar misschien niet bekend mee \ Verbeteren \ TestFixture syntaxis: wat is er met de slashes? Dat is PHP namespace voor jou; bekijk de documenten als u er niet bekend mee bent.

Dus, de tests!

E-mailadressen

Laten we beginnen met het valideren van e-mailadressen. Zoals u zult zien, is het eenvoudig om een ​​basistest uit te voeren:

 public function validates_a_good_email_address () $ result = $ this-> val-> validate_email ("[email protected]"); \ Verbeter \ Assert :: isTrue ($ result); 

We noemen simpelweg de methode die we willen testen, geven er een geldig e-mailadres aan en bewaren de $ result. Vervolgens geven we de hand $ result naar de is waar methode. Die methode behoort toe aan de \ Verbeteren \ Assert klasse.

We willen ervoor zorgen dat onze klas niet-e-mailadressen weigert. Laten we daarom testen:

 public function reject_bad_email_addresses () $ val_wrapper = \ Enhance \ Core :: getCodeCoverageWrapper ('Validation'); $ val_email = $ this-> get_scenario ('validate_email'); $ adressen = array ("john", "[email protected]", "john @ doe.", "jo*[email protected]"); foreach ($ adressen als $ addr) $ val_email-> with ($ addr) -> expect (false);  $ val_email-> verifyExpectations (); 

Dit introduceert een erg leuke functie van EnhancePHP: scenario's. We willen een aantal niet-e-mailadressen testen om er zeker van te zijn dat onze methode zal terugkeren vals. Door een scenario te maken, wikkelen we in wezen een instantie van onze klasse in een goede EnhancePHP-code, schrijven we veel minder code om al onze niet-adressen te testen. Dat is wat $ val_wrapper is: een gewijzigd exemplaar van onze bevestiging klasse. Dan, $ val_email is het scenario-object, een beetje als een snelkoppeling naar de email valideren methode.

Vervolgens hebben we een reeks tekenreeksen die niet moeten worden gevalideerd als e-mailadressen. We zullen die array doorlopen met een foreach lus. Merk op hoe we de test uitvoeren: we noemen het met methode op ons scenario-object, waarbij het de parameters doorgeeft voor de methode die we testen. Vervolgens noemen we de verwachten methode en geef het door wat we verwachten terug te krijgen.

Ten slotte noemen we de scenario's verifyExpectations methode.

Dus de eerste tests zijn geschreven; hoe kunnen we ze uitvoeren?


Tests uitvoeren

Voordat we de tests daadwerkelijk uitvoeren, moeten we onze bevestiging klasse. Binnen lib.validation.php, begin met dit:

  

Nu in test.php, we trekken het allemaal samen:

  

Ten eerste zullen we alle benodigde bestanden nodig hebben. Vervolgens noemen we de runTests methode, die onze tests vindt.

Vervolgens komt het nette gedeelte. Start een server op en je krijgt wat leuke HTML-uitvoer:

Heel leuk, toch? Als je nu PHP hebt in je terminal, voer je dit uit in de terminal:

EnhancePHP merkt dat u zich in een andere omgeving bevindt en past de uitvoer ervan op de juiste manier aan. Een bijkomend voordeel hiervan is dat als u een IDE, zoals PhpStorm, gebruikt die eenheidstests kan uitvoeren, u deze terminaluitvoer direct in de IDE kunt bekijken.

U kunt ook XML- en TAP-uitvoer krijgen, als dat is wat u wilt, gewoon doorgeven \ Verbeter \ TemplateType :: Xml of \ Verbeter \ TemplateType :: Tap naar de runTests methode om de juiste output te krijgen. Houd er rekening mee dat het uitvoeren van de opdracht ook opdrachtregelresultaten oplevert, ongeacht waar u aan overgaat runTests.

De tests laten slagen

Laten we de methode schrijven die ervoor zorgt dat onze tests slagen. Zoals je weet, dat is het email valideren. Aan de top van de bevestiging klasse, laten we een openbaar bezit definiëren:

 public $ email_regex = '/^[\w+-_\.]+@[\w\.]+\.\w+$/';

Ik zet dit in een openbaar bezit zodat als de gebruiker het met hun eigen regex wil vervangen, zij konden. Ik gebruik deze eenvoudige versie van een e-mailregex, maar je kunt deze vervangen door je favoriete regex als je dat wilt.

Dan is er de methode:

 openbare functie validate_email ($ address) return preg_match ($ this-> email_regex, $ address) == 1

Nu voeren we de tests opnieuw uit en:


Meer tests schrijven

Tijd voor meer tests:

gebruikersnamen

Laten we nu een aantal tests voor gebruikersnamen maken. Onze eisen zijn simpelweg dat het een tekenreeks van 4 tot 20 tekens moet zijn die alleen uit woordtekens of punten bestaat. Zo:

 public function validates_a_good_username () $ result = $ this-> val-> validate_username ("some_user_name.12"); \ Verbeter \ Assert :: isTrue ($ result); 

Nu, wat dacht je van een paar gebruikersnamen die niet moeten worden gevalideerd:

 public function rejects_bad_usernames () $ val_username = $ this-> get_scenario ('validate_username'); $ usernames = array ("naam met spatie", "no! exclaimation! mark", "ts", "thisUsernameIsTooLongItShezBeweenFourAndTwentyCharacters"); foreach ($ gebruikersnamen als $ naam) $ val_username-> met ($ naam) -> verwacht (false);  $ val_username-> verifyExpectations (); 

Dit lijkt erg op onze reject_bad_email_addresses functie. Merk echter op dat we dit noemen get_scenario methode: waar komt dat vandaan? Ik abstraheer de functionaliteit voor het maken van scenario's tot een privémethode, onderaan onze klas:

 persoonlijke functie get_scenario ($ methode) $ val_wrapper = \ Enhance \ Core :: getCodeCoverageWrapper ('Validation'); return \ Enhance \ Core :: getScenario ($ val_wrapper, $ methode); 

We kunnen dit gebruiken in onze reject_bad_usernames en vervang het maken van het scenario in reject_bad_email_addresses ook. Omdat dit een privémethode is, zal EnhancePHP niet proberen het als een normale test uit te voeren, zoals het zal doen met openbare methoden.

We laten deze tests op dezelfde manier verlopen als hoe we de eerste set hebben voltooid:

 # Bovenaan ... public $ username_regex = '/^^\w\.]4,20$/'; # en de methode ... public function validate_username ($ gebruikersnaam) return preg_match ($ this-> username_regex, $ username) == 1; 

Dit is natuurlijk vrij eenvoudig, maar dat is alles wat nodig is om ons doel te bereiken. Als we in het geval van een fout een verklaring zouden willen retourneren, zou je iets als dit kunnen doen:

 public function validate_username ($ gebruikersnaam) $ len = strlen ($ gebruikersnaam); als ($ len < 4 || $len > 20) return "Gebruikersnaam moet tussen 4 en 20 tekens zijn";  elseif (preg_match ($ this-> gebruikersnaam_regex, $ username) == 1) return true;  else return "Gebruikersnaam mag alleen letters, cijfers, underscores of punten bevatten."; 

Natuurlijk wilt u misschien ook controleren of de gebruikersnaam al bestaat.

Voer de tests uit en je zou ze allemaal moeten zien passeren.

Telefoonnummers

Ik denk dat je dit nu onder de knie hebt, dus laten we ons validatievoorbeeld afronden door de telefoonnummers te controleren:

 public function validates_good_phonenumbers () $ val_phonenumber = $ this-> get_scenario ("validate_phonenumber"); $ numbers = array ("1234567890", "(890) 123-4567", "123-456-7890", "123 456 7890", "(123) 456 7890"); foreach ($ getallen als $ num) $ val_phonenumber-> with ($ num) -> expect (true);  $ val_phonenumber-> verifyExpectations ();  public function rejects_bad_phonenumnbers () $ result = $ this-> val-> validate_phonenumber ("123456789012"); \ Verbeter \ Assert :: isFalse ($ result); 

Je kunt waarschijnlijk de bevestiging methode:

 public $ phonenumber_regex = '/ ^ \ d 10 $ | ^ (\ (? \ d 3 \)? [| -] \ d 3 [| -] \ d 4) $ /'; public function validate_phonenumber ($ number) return preg_match ($ this-> phonenumber_regex, $ number) == 1; 

Nu kunnen we alle tests samen uitvoeren. Dit is hoe dat eruit ziet op de opdrachtregel (mijn gewenste testomgeving):


Andere testfunctionaliteit

Uiteraard kan EnhancePHP veel meer doen dan wat we in dit kleine voorbeeld hebben bekeken. Laten we daar nu een deel van bekijken.

We hebben heel even de \ Verbeteren \ Assert klasse in onze eerste test. We hebben het niet echt anders gebruikt, omdat het niet handig is bij het gebruik van scenario's. Het is echter waar alle beweringsmethoden zijn. Het mooie van hen is dat hun namen hun functionaliteit ongelooflijk duidelijk maken. De volgende testvoorbeelden zouden slagen:

  • \ Enhance \ Assert :: areIdentical ("Nettuts +", "Nettuts +")
  • \ Enhance \ Assert :: areNotIdentical ("Nettuts +", "Psdtuts +")
  • \ Verbeter \ Assert :: isTrue (true)
  • \ Verbeter \ Assert :: isFalse (false)
  • \ Enhance \ Assert :: contains ("Net", "Nettuts +")
  • \ Verbeter \ Assert :: IsNull (null)
  • \ Verbeter \ Assert :: isNotNull ( 'Nettust +')
  • \ Enhance \ Assert :: isInstanceOfType ('Exception', new Exception (""))
  • \ Enhance \ Assert :: isNotInstanceOfType ('String', new Exception (""))

Er zijn ook enkele andere beweringsmethoden; u kunt de documentatie bekijken voor een volledige lijst en voorbeelden.

Mocks

EnhancePHP kan ook moppen en stubs doen. Heb je nog nooit van moppen en stubs gehoord? Nou, ze zijn niet al te ingewikkeld. Een mock is een wrapper voor een object, die kan bijhouden welke methoden worden aangeroepen, met welke eigenschappen ze worden genoemd en welke waarden worden geretourneerd. Een mock heeft een test om te verifiëren, zoals we zullen zien.

Hier is een klein voorbeeld van een schijnvertoning. Laten we beginnen met een heel eenvoudige klasse die telt:

 num = $ this-> num + $ num; return $ this-> num; 

We hebben één functie: aanwas, die een parameter accepteert (maar standaard 1) en verhoogt de $ num eigendom met dat nummer.

We zouden deze klasse kunnen gebruiken als we een scorebord zouden maken:

 class Scoreboard public $ home = 0; public $ away = 0; openbare functie __construct ($ home, $ away) $ this-> home_counter = $ home; $ this-> away_counter = $ away;  public function score_home () $ this-> home = $ this-> home_counter-> increment (); return $ this-> home;  public function score_away () $ this-> away = $ this-> away_counter-> increment (); return $ this-> home; 

Nu willen we testen om ervoor te zorgen dat het Teller instantie methode aanwas werkt naar behoren als het Scorebord instantiemethoden noemen het. Dus we maken deze test:

 class ScoreboardTest breidt \ Enhance \ TestFixture uit openbare functie score_home_calls_increment () $ home_counter_mock = \ Enhance \ MockFactory :: createMock ("Counter"); $ away_counter = nieuwe teller (); $ home_counter_mock-> addExpectation (\ Enhance \ Expect :: -methode ('increment')); $ scoreboard = nieuw scorebord ($ home_counter_mock, $ away_counter); $ Scoreboard-> score_home (); $ Home_counter_mock-> verifyExpectations ();  \ Enhance \ Core :: runTests ();

Merk op dat we beginnen met creëren $ home_counter_mock: we gebruiken de mock-fabriek EnhancePHP en geven deze de naam van de klas die we bespotten. Dit retourneert een "ingepakt" exemplaar van Teller. Vervolgens voegen we een verwachting toe, met deze regel

 $ home_counter_mock-> addExpectation (\ Enhance \ Expect :: -methode ('increment'));

Onze verwachting zegt alleen dat we de aanwas methode die moet worden aangeroepen.

Daarna gaan we door met het maken van de Scorebord bijvoorbeeld, en bel score_home. Dan gaan we verifyExpectations. Als u dit uitvoert, ziet u dat onze test slaagt.

We kunnen ook aangeven welke parameters we willen dat een methode om het onechte-object te gebruiken, welke waarde wordt geretourneerd of hoe vaak de methode moet worden aangeroepen, met zoiets als dit:

 $ home_counter_mock-> addExpectation (\ Enhance \ Expect :: -methode ('increment') -> met (10)); $ home_counter_mock-> addExpectation (\ Enhance \ Expect :: -methode ('increment') -> times (2)); $ home_counter_mock-> addExpectation (\ Enhance \ Expect :: methode ('increment') -> geeft als resultaat (1)); $ home_counter_mock-> addExpectation (\ Enhance \ Expect :: -methode ('increment') -> met (3) -> tijden (1)); $ home_counter_mock-> addExpectation (\ Enhance \ Expect :: -methode ('increment') -> met (2) -> returns (2));

Ik zou dat moeten vermelden, terwijl met en tijden zal mislukte tests laten zien als de verwachtingen niet zijn bedoeld, komt terug niet. Je zult de geretourneerde waarde moeten opslaan en een bewering gebruiken om dat te bereiken. Ik weet niet zeker waarom dat zo is, maar elke bibliotheek heeft zijn eigenaardigheden :). (U kunt een voorbeeld hiervan zien in de bibliotheekvoorbeelden in Github.)

stubs

Dan zijn er stubs. Een stomp vult in voor een echt object en een echte methode, en retourneert precies wat u zegt. Laten we zeggen dat we er zeker van willen zijn dat onze Scorebord instantie gebruikt correct de waarde van waaruit het ontvangt aanwas, we kunnen a Teller Zo kunnen we bepalen wat aanwas zal terugkeren:

 class ScoreboardTest breidt \ Enhance \ TestFixture uit openbare functie score_home_calls_increment () $ home_counter_stub = \ Enhance \ StubFactory :: createStub ("Counter"); $ away_counter = nieuwe teller (); $ home_counter_stub-> addExpectation (\ Enhance \ Expect :: methode ('increment') -> geeft als resultaat (10)); $ scoreboard = nieuw scorebord ($ home_counter_stub, $ away_counter); $ result = $ scoreboard-> score_home (); \ Enhance \ Assert :: areIdentical ($ result, 10);  \ Enhance \ Core :: runTests ();

Hier gebruiken we \ Verbeter \ StubFactory :: createStub om onze stub-teller te maken. Vervolgens voegen we een verwachting toe dat de methode aanwas zal terugkeren 10. We kunnen zien dat het resultaat dat is wat we zouden verwachten, gezien onze code.

Voor meer voorbeelden van mocks en stub met de EnhancePHP-bibliotheek, bekijk de Github Repo.


Conclusie

Nou, dat is een blik op testen in PHP, met behulp van het EnhancePHP-framework. Het is een ongelooflijk eenvoudig raamwerk, maar het biedt alles wat je nodig hebt om eenvoudige eenheidscontroles uit te voeren op je PHP-code. Zelfs als je een andere methode / raamwerk kiest voor het testen van je PHP (of misschien je eigen rol!), Hoop ik dat deze tutorial interesse heeft in het testen van je code en hoe eenvoudig het kan zijn.

Maar misschien test je je PHP al. Laat ons allemaal weten wat u in de commentaren gebruikt; we zijn tenslotte allemaal hier om van elkaar te leren! Heel erg bedankt voor het langskomen!