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
Zaznaczone na czerwono metody są do implementacji w pierwszej koloejności.
![CLD: AuthBasic](lib/utils/displayImage.php?id=tida&d=40-unit-tests&f=cld-AuthBasic.png)
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
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');
}
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;
}
/*
* */
}