2024-06-30 23:56:23
  • Hello!
  • Whats ya doin?
  • ByeBye

[&]

3.145.48.33

Przykład. Autoryzacja użytkownika

workshop: #40-php-unit.html

Testy jednostkowe. PHP Unit

Warsztat: Testy systemu autoryzacji - przykładowa klasa

Przygotowanie

  1. Utwórz katalog do pracy 40-php-unit
  2. Pobierz do w/w katalogu bibliotekę do testów PHPUnit
  3. Stwórz szkielet klasy autoryzacyjnej zgodnej z załączonym diagramem UML
  4. 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.

CLD: AuthBasic

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;
}
/*
 * */
}

Podsumowanie

  1. Zaimplementuj test dla metody verifyQuickRegCode()
  2. Jak przygotowanie testu przed implementacją kodu klasy pomogło w jej implementacji?
  3. Jak użycie komentowania kodu w DocBlock pomogło w implementacji klasy?