Testy jednostkowe. PHP Unit Warsztat: Testy systemu autoryzacji - przykładowa klasa Przygotowanie Utwórz katalog do pracy 40-php-unit Pobierz do w/w katalogu bibliotekę do testów PHPUnit Stwórz szkielet klasy autoryzacyjnej zgodnej z załączonym diagramem UML Jednocześnie implementując klasę AuthBasic wykonaj dokumentację kodu stosując standard DocBlock 1 Diagram klasy AuthBasic Zaznaczone na czerwono metody są do implementacji w pierwszej koloejności. Po implementacji szkieletu klasy AuthBasic należy przejść do przygotowywania (pliku/klasy) testu jednostkowego. Zakładana struktura katalogów: / 40-php-unit |-- app | \-- AuthBasic.php |-- tests | \-- AuthBasicTest.php |-- tool | \-- phpunit.phar Narzędzie wywoływane z poziomu katalogu głównego 40-php-unit 2 Kod testu jednostkowego do klasy AuthBasic Kroki realizacji testu: Szkielet klasy testowej z załączeniem Frameworka PHPUnit (jako Trait) oraz z załączeniem pliku testowanej klasy Przygotowanie metod inicjujących instancje testowanej klasy oraz niszczących tę instancję po każdym teście Implementacja 1-szej testowanej metody i asercji dla niej Implementacja 2-giej testowanej metody i asercji dla niej <?php // załącz plik testowanej klasy - dopasuj ścieżkę do pliku zgodną z własną strukturą katalogów require('app/AuthBasic.php'); // użycie wbudowanych testów use PHPUnit\Framework\TestCase; // nazwanie i rozszerzenie własnej klasy klasą `TestCase` zawierającą Asercje do testów class AuthBasicTest extends TestCase { // tutaj umieść kod testów (metody) } Jeżeli każdy wywoływany test nie jest stricte zależny od innego, czyli nie przekazuje danych pomiędzy funkcjami - używamy metod wywołujących instancję testowanej klasy i niszczącą ją po wykonaniu każdego testu. public function setUp() : void { $this->instance = new AuthBasic(); } public function tearDown() : void { unset($this->instance); } Jeżeli poprzez testy chcemy zachować stan i dane klasy, neleży: class AuthBasicTest extends TestCase { $auth = new AuthBasic(); // tutaj umieść kod testów (metody) } Pisanie testów i asercji - najpierw tworzymy testy: generowanie kodu/numeru autoryzacyjnego wg. wybranego algorytmu generowanie Tokenu zawierającego zestaw informacji o Osobie oraz wygenerowany dla niej Kod autoryzacyjny public function testCreateCode(){} public function testCreateAuthToken(){} Testowanie tworzenia krótkiego Kodu autoryzacyjnego wg. zadanych parametrów. Należy przetestować następujace warianty: Kod wygenerowany wg. domyślnych parametrów Kod wygenerowany dla długości 4 znaków (mniej niż domyślna długość) Kod wygenerowany dla długości 8 znaków (więcej niż domyślna długość) public function testCreateCode(){ $out = $this->instance->createCode(); // jezeli potrzeba wyświetlić cokolwiek w widoku testu, należy użyć: fwrite(STDERR, print_r($out, true)); $len = strlen($out); $this->assertIsNumeric($out,'Wylosowano: '.$out); $this->assertEquals(6,$len,'Długość: '.$len); $out = $this->instance->createCode(4); $len = strlen($out); $this->assertIsNumeric($out,'Wylosowano: '.$out); $this->assertEquals(4,$len,'Długość: '.$len); // symulowanie wylosowania liczby o mniejszej niż oczekiwana długość, którą należy uzupełnić zerami // nie można liczyć, że podczas testu zawsze wygenerujemy taką liczbą, stąd skopiowanie implementacji metody $out = str_pad(1111,6,'0',STR_PAD_LEFT); $len = strlen($out); $this->assertIsNumeric($out,'Wylosowano: '.$out); $this->assertEquals(6,$len,'Długość: '.$len); } Testowanie tworzenia Tokenu autoryzacyjnego public function testCreateAuthToken(){ // oczekiwana struktura tokenu z następującymi informacjami $exp = array( 'emlAuth'=>'gpetri@gplweb.pl','authCode'=>'131313', 'authDate'=>date("Y-m-d"),'authHour'=>date("H:i:s"), 'addrIp'=>'127.0.0.1','reqOs'=>'Linux','reqBrw'=>'FF' ); // wywołanie testowanej metody z przykładowymi danymi użytkownika: email i jego IDentyfikator $out = $this->instance->createAuthToken('gpetri@gplweb.pl',13); // ponieważ generowany Token jest wartością losową - musimy go napisać wartością stałą - inaczej nie ma możliwości wykonania pomyślnie testu $out['authCode'] = '131313'; // wywołanie testu właściwego - Asercji (założenia) $this->assertEquals($exp,$out,'Tablice są różne'); } Testowanie generowanego odcisku palca. Jak przetestować wartość losową? sprawdzić typ danych, np. String sprawdzić długość wygenerowanego Hash'a czy jest faktyczna potrzeba testowania Takiej funkcji? public function testGenFingerprint(){ // ?? $this->assertEquals($exp,$out,'Tablice są różne'); } 3 Kod i komentarz do klasy AuthBasic Dokumentacja kodu wykonana zgodnie ze standardem DocBlock <?php /** * Klasa do autoryzacji jednorazowego dostępu do fragmentu serwisu * @author Grzegorz Petri * @since 0.2 */ class AuthBasic { public function genFingerprint($algo){ } /** * @desc Generuje kod wymagany do podania podczas autoryzacji dostępu, wg. podanych parametrów * @param int $length Długość kodu - liczba znaków * @param int $min Minimalna wartość dla generowanego numeru * @param int $max Maksymalna wartość dla generowanego numeru * @return int Zwraca wygenerowaną na podstawie parametrów liczbę, która musi zostać uzupełniana zerami, jeżeli trzeba spełnić długość */ public function createCode( $length=6, $min=1, $max=999999 ){ $max = substr($max,0,$length); return str_pad(mt_rand($min,$max),$length,'0',STR_PAD_LEFT);// losowanie 1-999999 } #private function createAuthToken( $email, $id ){} public function compAuthCode( $emlAuth, $idzAuth, $authCode ){} public function doAuthByEmail( $person, $email ){} public function checkIfValidReqest( $person, $email ){} private function checkIfValidReqest2f( $emlAuth, $idzAuth ){} public function verifyQuickRegCode($codeNo){} /** * @desc Tworzy wpis w BD z numerem pozwalającym na uwierzytelnienie Requesta * Tworzony Token do uwierzytelnienia zapisując adres Email oraz ID użytkownika * Token musi zostać wysłany na pocztę użytkownika, stąd zwracany jest Obiekt informacyjny * @param string $email Adres email użytkownika do uwierzytelnienia * @param int $id Numer ID użytkownika do uwierzytelnienia * @return array|false Wygenerowany Token LUB Fałsz */ public function createAuthToken( $email, $id){ $authCode = $this->createCode(); $authDate = date("Y-m-d"); $authHours = date("H:i:s"); $addrIp = '127.0.0.1'; #TODO ->code $opSys = 'Linux';#TODO @see whichBrowser $browser = 'FF';#TODO @see whichBrowser $cont = array( 'emlAuth'=>$email,'authCode'=>$authCode, 'authDate'=>$authDate,'authHour'=>$authHours, 'addrIp'=>$addrIp,'reqOs'=>$opSys,'reqBrw'=>$browser ); # START >> db->put() $tbl = 'cmsWebsiteAuth'; $cols = 'session_id, usrId, addrIp, fingerprint, dateTime, content, email, authCode'; $vals = '1234567890,$id,$addrIp,hash_hmac(sha512+USER_AGENT+hash()+TRUE),$dt,0,$eml,$code'; $file = dirname(__FILE__).'/db.txt'; file_put_contents($file,serialize($cont)); $fData = file_get_contents($file); #var_dump(unserialize($fData)); # STOP >> db->put() $tok = (unserialize($fData)==$cont) ? 0 : 'err:1045'; $resp = ($tok===0) ? $cont : false; return $resp; } /* * */ }