vežbe -xiinedelja-...
TRANSCRIPT
Vežbe - XII nedelja -Testiranje veb aplikacija
Dražen Drašković, docentUniverzitet u Beograd – Elektrotehnički fakultetOdsek za softversko inženjerstvo
Kod tehnika crne kutije analiziramo samo funkcionalnosti na osnovu funkcionalne specifikacije definisanih zahteva
Koristimo iz projekta:Dokument projektni zadatak (iz faze 1)
Specifikaciju svih slučajeva upotrebe (iz faze 2)
Primenjujemo metodu klasa ekvivalencije (eng. Equivalence class partitioning)
Realizujemo test plana sa test primerima
Automatizovano testiranje korišćenjem nekog alata i provera ponašanja u različitim pregledačima
Ideja: podela ulaznih podataka na podskupove (klase)
Svi ulazni podaci koji daju iste (slične) rezultate pripadaju istoj reprezentativnoj klasi
Idealni slučaj: podskupovi međusobno disjunktni i pokrivaju ceo skup ulaznih podataka
Za svaki uslov se posmatraju dve grupe klasa:Legalne klase - obuhvataju ispravne situacije (ulazne podatke)
Nelegalne klase - obuhvataju sve ostale situacije (ulazne podatke)
Prednost ovog metoda: vreme potrebno za testiranje
Ulazni podatak na osnovu opsega:jedna legalna klasa (unutar opsega)
dve nelegalne klase (levo i desno od opsega)
Primer: mesec uzima broj iz opsega 1-12
Ulazni podatak na osnovu dužine ili formatajedna legalna klasa (tačan broj karaktera/cifara)
dve nelegalne klase (za manji/veći broj karaktera)
Primer: JMBG ima tačno 13 cifara
Tačno definisan skup ulaznih podatakapo jedna legalna klasa (za svaku pojedinačnu vrednost) i jedna zajednička nelegalna klasa
Uslov obaveznosti za ulazni podatakjedna legalna klasa za ispravnu vrednost
i bar jedna nelegalna klasa za neispravnu vrednost
U slučaju da program ne tretira jednako sve elemente neke klase ekvivalencije, nju treba podeliti na više manjih klasa
Test plan (eng. Test suite) sastoji se iz test primera (eng. Test case)
Svaki test primer treba da bude dizajniran pre implementacije i izvršavanja testa
Nakon što se odrede klase ekvivalencije, formiraju se test primeri za:
sve legalne klase ekvivalencije (cilj je da se što manje test primera primeni na što više klasa ekvivalencije)
sve nelegalne klase ekvivalencije (za svaku nelegalnu klasu po jedan test primer, da se ne bi desilo da jedan test primer koji nije regularan maskira neki drugi test primer)
Opciono možemo koristiti i metod graničnih slučajeva (eng. Boundary value testing)
Metod klasa ekvivalencije proširujemo, tako da se fokusiramo na granice svake klase, jer se tu krije veliki broj grešaka
Primer: popust na avio karte u kompaniji Air Serbia, za decu do 7 godina, da li je popust ako dete nije napunilo 7, ili u celoj kalendarskoj godini, dok ne napuni 8 god.
Identifikujemo klase ekvivalencije, a zatim identifikujemo granice svake klase ekvivalencije.
Pravimo test primer za svaku graničnu vrednost tako što biramo:
Jednu tačku A na samoj granici
Jednu tačku B ispod granice
Jednu tačku C iznad granice
Tačke B i C treba vrlo pažljivo odabrati i te tačke zavise od jedinice vrednosti podatka koji analiziramo. Takve tačke mogu biti i u drugim klasama ekvivalencije, pa voditi računa da testovi NE BUDU DUPLIRANI.
Manuelno testiranje
Automatsko GUItestiranje (web automation testing)
Primer: - Selenium IDE framework- iMacros
Postoji i Selenium WebDriver:
kompaktni OO API
za različite veb pregledače(cross browser testing)
U literaturi još i kao: strukturno testiranje (eng. structural testing),
testiranje čistom kutijom (eng. clear box),
otvorenom kutijom (eng. open box),
staklenom/transparentnom kutijom (eng. glass box / transparent box),
zasnovano na kodu (eng. code-based testing)
Primarni izvor za projektovanje testova je izvorni kod sa fokusom na tok kontrole i tok podataka
Cilj strukturnog testiranja nije da se izvrše sve moguće funkcije programa, već da se izvrše/aktiviraju različite programske i strukture podataka u programu
Grafovi kontrole toka predstavljaju vizuelnu reprezentaciju strukture koda nekog programa. Izvršavanjem programa (na primer, za neke test podatke) vrši se izbor neke putanje u grafu.
Vrste:Pokrivenost iskaza (eng. Statement Coverage) = pokrivenost instrukcija ili pokrivenost koda
Pokrivenost odluka ili pokrivenost grana (eng. Decision or Branch Coverage)
Pokrivenost uslova (eng. Condition Coverage)
Pokrivenost višestrukih uslova (eng. Multiple Condition Coverage)
Minimalna pokrivenost višestrukih uslova (eng. Minimal Multiple Condition Coverage)
Pokrivenost odluka i uslova (eng. Decision/Condition Coverage)
Modifikovana pokrivenost odluka i uslova (MC/DC)
Jedinično testiranje (eng. unit testing)
Integraciono testirane (eng. integration testing)
Sistemsko testiranje (eng. system testing)
PHPUnit je alat otvorenog koda za automatizovano jedinično testiranje PHP koda
Alat je napisan u PHP-u i koncepcijski zasnovanna JUnit-u (za Javu)
Najnovija verzija: PHPUnit ver. 9.1 (februar 2020) baziran za testiranje PHP 7.3 ili 7.4
Instalacija po projektu, ne globalno!
Nema potrebe da bude na veb serveru. Zašto?
Dokumentacija dostupna na:https://phpunit.readthedocs.io/en/9.1/
Šta dobijamo jediničnim testovima?identifikovanje grešaka i ispravke u kodu
refaktorizacija koda
dokumentacija za najmanje elemente programskog koda (koji se testiraju)
Da bi sve ovo postigli, moramo da pokrijemo sve putanje u kodu
Jedan test obično pokrije jednu putanju u funkciji
Analiza pokrivenosti koda (code coverage)
Izveštaj o pokrivenosti koda zahteva Xdebug (ver. 2.7.0)
Tipovi obuhvaćeni kroz PHPUnit:Pokrivenost iskaza (Line Coverage)
Pokrivenost funkcija (Function/Method Coverage)
Pokrivenost klasa i osobina (Class and Trait Coverage)
Pokrivenost iskompajliranog koda (Opcode Coverage)
Pokrivenost grana (Branch Coverage)
Pokrivenost bazičnih putanja (Path Coverage)
Change Risk Anti-Patterns (CRAP) Index
Automatsko generisanje izveštaja o izvršenimtestovima u XML formatu
Analiza pokrivenosti koda
Prihvatanje proizvoljnog broja argumenata u testovima (parametrizovani testovi)
Podrška za nekompletne testove (tela nekih test funkcija su prazna) i preskakanje testova
Generisanje skeleta test koda za postojeći kod
Podrška za mock objekte
Integracija sa raznim alatima
Cilj je da postoji dobra struktura, tako da možemo pokretati odvojeno grupe:
testovi za ceo projekat
testovi za sve klase neke komponente
samo testovi za jednu klasu
Obavezno odvojeno izvorni kod i testovi$ phpunit --bootstrap src/autoload.php tests
PHPUnit 9.1.0 by Sebastian Bergmann and
contributors.
.................................
Time: 636 ms, Memory: 3.50Mb
OK (33 tests, 52 assertions)
phpunit.xml - XML format fajla i navodimo test primere<phpunit bootstrap="src/autoload.php">
<testsuites>
<testsuite name="grupa1">
<file>tests/IntlFormatterTest.php</file>
<file>tests/MoneyTest.php</file>
<file>tests/CurrencyTest.php</file>
</testsuite>
</testsuites>
</phpunit>
<phpunit bootstrap="src/autoload.php">
<testsuites>
<testsuite name="grupa2">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>
Test sa višestrukim zavisnostima Parametrizovani testovi
$ phpunit DataTest
PHPUnit 9.1.0 by Sebastian Bergmann and contributors.
..F... 6 / 6 (100%)
Time: 0 seconds, Memory: 5.75Mb
There was 1 failure:
1) DataTest::testAdd with data set #3 (1, 1, 3)
Failed asserting that 2 is identical to 3.
/home/sb/DataTest.php:12
FAILURES!
Tests: 6, Assertions: 6, Failures: 1.
Izlaz testa:
$ phpunit --verbose SampleTest
PHPUnit 9.1.0 by Sebastian Bergmann and contributors.
I
Time: 0 seconds, Memory: 3.95Mb
Izlaz testa:There was 1 incomplete test:
1) SampleTest::testSomething
This test has not been implemented yet.
/home/sb/SampleTest.php:12
OK, but incomplete or skipped tests!
Tests: 1, Assertions: 1, Incomplete: 1.
Nekompletan test
Preskakanje testa
$ phpunit --verbose DatabaseTest
PHPUnit 9.1.0 by Sebastian Bergmann and contributors.
S
Time: 0 seconds, Memory: 3.95Mb
There was 1 skipped test:
1) DatabaseTest::testConnection
The MySQLi extension is not available.
/home/sb/DatabaseTest.php:9
OK, but incomplete or skipped tests!
Tests: 1, Assertions: 0, Skipped: 1.
Izlaz testa:
Cilj: realni deo koda, tzv. depended-on component(DOC) zameniti sa test dublerom, koji se ponaša isto kao pravi deo koda.
Koriste se metode:createStub($type),
createMock($type),
getMockBuilder($type).
Vraćaju objekat test dublera za specifični tip (klasu)
Podrazumevano, sve metode originalne klase se zamenjuju sa dummy implementacijama koje vraćaju vrednost null
Praksa zamene objekata sa test dublerom, koji opciono vraća predefinisane vrednosti zove se stubbing.
Stub se koristi da zameni realnu komponentu od koje zavisi sistem i proces testiranja, tako da test ima kontrolne tačke za indirektne ulaze u sistem
Na taj način, sistem će pomoću stubs izvršiti i putanje, koje inače ne bi mogao
Izuzetak: (ako se metoda baš zove method)$stub->expects($this->any())->method('doSomething')->willReturn('foo');
Praksa zamene objekata sa test dublerom, koji potvrđuje očekivanja na primer tvrdeći da je metoda pozvana, zove se mocking.
Mock se koristi kao posmatrana tačka za verifikaciju indirektnih izlaza sistema koji se testira
Mock objekat uključuje funkcionalnost test stub-a, u koji on mora vratiti vrednost sistemu, ako nisu svi pokrenuti testovi pali
PHPUnit se može instalirati korišćenjemPHP Archive (PHAR), njenim preuzimanjem i raspakivanjem:
Može se dodati u projekat preko Composer-a
Rezultati se najčešće prikazuju u izveštaju o testiranju, koji je takođe u XML formatu kod PHPUnit alata (slično kao kod JUnit za Apache Ant)
Ako postoje greške, one će se jasno prikazati kao failure ili error
<?xml version="1.0" encoding="UTF-8"?>
<coverage generated="1184835473" phpunit="3.6.0">
<project name="BankAccountTest" timestamp="1184835473">
<file name="/home/sb/BankAccount.php">
<class name="BankAccountException">
<metrics methods="0" coveredmethods="0" statements="0"
coveredstatements="0" elements="0" coveredelements="0"/>
</class>
<class name="BankAccount">
<metrics methods="4" coveredmethods="4" statements="13"
coveredstatements="5" elements="17" coveredelements="9"/>
</class>
<line num="77" type="method" count="3"/>
<line num="79" type="stmt" count="3"/>
<line num="89" type="method" count="2"/>
</file>
<metrics files="1" loc="126" ncloc="37" classes="2" methods="4"
coveredmethods="4" statements="13" coveredstatements="5"
elements="17" coveredelements="9"/>
</project>
</coverage>
Assertation metode se deklarišu statičke i mogu se pozvati iz bilo kog konteksta za instancu:
PHPUnit\Framework\Assert::assertTrue()
$this->assertTrue() ili self::assertTrue(),
Još lakše se može pozvati kao omotač globalne funkcije, u bilo kom kontekstu, npr. assertTrue()
Samo uključiti: src/Framework/Assert/Functions.php
Šta je bolje koristiti?Programeri najviše vole: $this->assertTrue()
Sve funkcije su oblika: assertNesto
assertArrayHasKey()
assertClassHasAttribute()
assertContains()
assertCount()
assertDirectoryExists()
assertDirectoryIsReadable()
assertDirectoryIsWritable()
assertEmpty()
assertEquals()
assertFalse()
assertTrue()
assertFileExists()
assertGreaterThan()
assertGreaterThanOrEqual()
assertInfinite()
assertIsArray()
assertIsFloat()
assertIsInt()
assertIsNumeric()
assertIsObject()
assertIsString()
assertLessThan()
assertNan()
assertNull()
assertMatchesRegularExpression()
assertStringMatchesFormat()
assertXmlFileEqualsXmlFile()
assertEquals(mixed $expected, mixed $actual
[, string $message=‘’])
Prijavljuje neuspeh, ako se $expected i $actual razlikuju
$message je opciona poruka koja se ispisuje u slučaju neuspeha
Inverzna f-ja: assertNotEquals, sa istim argumentima
F-ja: assertEqualsWithDelta(mixed $expected, mixed $actual, float $delta
[, string $message = ''])
$delta je opciona numerička vrednost koja se koristi kada su $expected i $actual nekog numeričkog tipa. Tada se neuspeh prijavljuje ako nije zadovoljen uslov: $actual € [$expected - delta, $expected + $delta]
assertContains(mixed $needle,
iterable $haystack[, string $message = ''])
Prijavljuje neuspeh, ako $haystack ne sadrži $needle, pričemu ove promenljive mogu biti stringovi, ili je $haystack niz, a $needle nekog prostog tipa
$message je opciona poruka, u slučaju neuspeha
Primer:<?php
use PHPUnit\Framework\TestCase;
class ContainsTest extends TestCase {
public function testFailure(){
$this->assertContains(4, [1, 2, 3]);
}
}
Inverzna f-ja:assertNotContains(mixed $needle,
iterable $haystack[, string $message = ''])
Prijavljuje neuspeh, ako $haystack sadrži $needle
Postoje još neke funkcije:
assertStringContainsString(string $needle,
string $haystack[, string $message = ’’])
Ova funkcija ispisuje grešku u vidu poruke ukoliko argument $needle nije podstring argumenta $haystack
Inverzna: assertStringNotContainsString()
Ako zanemarujemo mala i velika slova:assertStringContainsStringIgnoringCase(
string $needle, string $haystack[,
string $message = ''])
assertNull(mixed $variable[,
string $message = ''])
Prijavljuje neuspeh, ako $variable nije NULL
$message je opciona poruka, ispisuje se u slučajuneuspeha
Inverzna f-ja: assertNotNull()
Prijavljajuje neuspeh, ako je $variable NULL.
assertTrue(bool $condition
[, string $message = ''])
Prijavljuje neuspeh, ako $condition nije true
$message je opciona poruka, u slučaju neuspeha
Inverzna f-ja: assertNotTrue()
iliassertFalse(bool $condition
[, string $message = ’’])
Prijavljuje neuspeh, ako $condition nije false.
assertMatchesRegularExpression(
string $pattern, string $tekst[,
string $message = ''])
Prijavljuje neuspeh, ako se string $tekst ne poklapa sa regularnim izrazom $pattern
$message je opciona poruka koja se ispisujeu slučaju neuspeha
Inverzna funkcija:assertDoesNotMatchRegularExpression()
sa istim argumentima
assertIsObject($actual[, $message = ''])
assertIsString($actual[, $message = ’’])
assertIsInt($actual[, $message = ’’])
assertIsFloat($actual[, $message = ’’])
assertIsBool($actual[, $message = ’’])
assertIsResource($actual[, $message = ’’])
Za nizove:assertArrayHasKey(mixed $key,
array $array [, string $message = ’’])
assertCount($expectedCount, $haystack [,
string $message = ''])
assertClassHasAttribute(string $imeAtributa,
string $imeKlase [, string $message = ''])
Prijavljuje neuspeh i ispisuje poruku,ako ne postoji$imeKlase::imeAtributa
Inverzna f-ja sa istim argumentima: assertClassNotHasAttribute()
Primer:<?php
use PHPUnit\Framework\TestCase;
class ClassHasAttributeTest extends TestCase {
public function testFailure(){
$this->assertClassHasAttribute(’atr', stdClass::klasa);
}
}
@author
@after
@afterClass
@backupGlobals
@backupStaticAttributes
@before
@beforeClass
@codeCoverageIgnore
@covers
@coversDefaultClass
@coversNothing
@dataProvider
@depends
@doesNotPerformAssertions
@group
@large
@medium
@preserveGlobalState
@requires
@runTestsInSeparate-
Processes
@runInSeparateProcess
@small
@test
@testdox
@testWith
@ticket
@uses
Inicijalizacija:$this->load->library('unit_test');
Pokretanje testa:$test = 1 + 1;
$expected_result = 2;
$test_name = 'Adds one plus one';
$this->unit->run($test, $expected_result,
$test_name);
Ispisivanje jednog testa:echo $this->unit->run($test,
$expected_result);
Ispisivanje izveštaja svih testova:echo $this->unit->report();
Ispisivanje izveštaja u vidu HTML tabele:echo $this->unit->result();
Kada se pokrene rezultat izvršavanja jediničnog testa prikazuje sledeće podatke:
Test Name (test_name)
Test Datatype (test_datatype)
Expected Datatype (res_datatype)
Result (result)
File Name (file)
Line Number (line)
Any notes you entered for the test (notes)
Izveštaj se može uređivati:$this->unit->set_test_items(array('test_name',
'result'));
U trenutku pisanja programskog koda (test driven development)
Na taj način utvrđujemo manje bagove u kodu još tokom razvoja
Praksa je da programeri ne testiraju svoj kod, jer tako previde svoje greške, već da deo koda koji je on implementirao, testira drugi programer ili osoba iz tima koja se bavi samo testiranjem (softverski tester)
Na našem projektu:u toku faze implementacije: pišemo jedinične testove i testiramo najniže delove koda (bela kutija)
nakon završetka implementacije: pišemo automatizovane testove i testiramo celokupan sistem (crna kutija)
PHP osnovna stranica za dokumentaciju:http://www.php.net
PHP Unit dokumentacija:https://phpunit.readthedocs.io/en/9.1/index.html
Testiranje softvera, sajt predmeta na ETF-u:http://si3ts.etf.bg.ac.rs
Jedinično testiranje u okviruCodeIgniter radnog okvira:https://codeigniter.com/userguide3/libraries/unit_testing.html