SlideShare a Scribd company logo
1 of 39
Download to read offline
Wprowadzenie do PHPUnit
By Michał Kowalik
Czym są testy jednostkowe?
● W programowaniu metoda testowania tworzonego
oprogramowania poprzez wykonywanie testów
weryfikujących poprawność działania pojedynczych
elementów (jednostek) programu.
● Wyróżniamy następujące rodzaje:
- testy jednostkowe
weryfikacja kodu
- testy integracyjne
weryfikacja komunikacji z zasobami np. bazą danych
- testy funkcjonalne /end-to-end/
weryfikacja wymagań użytkownika
Korzyści
● Są automatyczne
Odbywają się za nas, nie musimy pamiętać by ręcznie sprawdzić jakiś
tam jeszcze edge-case.
● Bardzo dobrze wpływają na jakość kodu
Dzielimy program na mniejsze klocki.
Zaczynamy korzystać z wzorców projektowych.
Za każdym razem musimy odpowiedzieć sobie na pytanie Jak ja to
potem przetestuje?
Korzyści
● Pozwalają wykrywać problemy na etapie tworzenia
aplikacji.
● Skracają czas programowania.
Nie musimy przeskakiwać do przeglądarki i tracić czasu na
ręczne testy. Możemy pracować bez odrywania się od IDE.
Korzyści
● PHP jest dynamicznym językiem
Wykrywanie błędów składni, nieistniejących metod, niewłaściwego
wykorzystania typów, błędnego wykorzystania funkcji wbudowanych
● Weryfikacja działania na różnych platformach
Programiści mogą pracować na Windowsach, ale serwery są
zazwyczaj na Linuxach
Co pewien czas wychodzi nowa wersja PHP, testy pozwalają
nam sprawdzić czy program zadziała w nowym środowisku (np.
w HHVM)
Obecność testów jest konieczna do wykorzystania narzędzi typu
Continous Integration (Github / Travis).
Korzyści
● Testy nabierają znaczenia gdy nasz projekt rośnie
Początkowo testy mogą wydawać się zbędę i niepotrzebnie nas
obciążać
W marę jak rozbudowujemy projekt testy pozwalają
zweryfikować czy zmiany nie uszkodziły przedniej
funkcjonaliści
Pozwalają nowemu programiści w zespole sprawdzić –
samodzielnie – czy czegoś nie zepsuł.
Korzyści tylko wtedy gdy:
● Testy działają szybko
● Nie generują dodatkowych błędów
Są napisane w możliwie prosty sposób
● Są w stanie przetrwać ew. modyfikacje kodu
Testy nie mogą być zbyt szczegółowe
● Generalnie pisanie testów jest sztuką samą w sobie
Oddzielna specjalizacja
Instalacja (phar) - zalecana
#unix
wget https://phar.phpunit.de/phpunit.phar
chmod +x phpunit.phar
mv phpunit.phar phpunit
#phpunit.bat
c:phpphp.exe c:phpphpunit.phar %*
#sprawdzenie poprawności
> phpunit –-version
PHPunit 4.1.0 by Sebastian Bergmann.
Instalacja 3.7 (pear) - przestarzała
pear update-channels
pear config-set auto_discover 1
pear channel-discover pear.phpunit.de
pear install --alldeps --force phpunit/PHPUnit
pear install --alldeps --force phpunit/DbUnit
pear install --alldeps --force phpunit/PHPUnit_Selenium
pear install --alldeps --force phpunit/PHPUnit_SkeletonGenerator
pear install --alldeps --force phpunit/PHPUnit_Story
pear install --alldeps --force phpunit/PHP_CodeCoverage
pear install --alldeps --force phpunit/PHP_Invoker
Konfiguracja
● phpunit.xml
Dzięki niemu możemy skonfigurować środowisko w jednym
miejscu i uruchomić testy poprzez ./phpunit
● bootsrap.php
W tym piku inicjujemy projekt (ustawiamy globalne zmienne,
class loadery, startujemy framework).
Bardzo często zawiera prawie to samo co
public/index.php
Położenie można ustalić w phpunit.xml
phpunit.xml
<?xml version="1.0"
encoding="UTF-8"?>
<phpunit
backupGlobals="true"
backupStaticAttributes="true"
bootstrap="bootstrap.php"
cacheTokens="false"
colors="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
stopOnIncomplete="false"
stopOnSkipped="false"
timeoutForSmallTests="1"
timeoutForMediumTests="10"
timeoutForLargeTests="60"
strict="false"
verbose="false"
>
<testsuites>
<testsuite name="Kohana Tests">
<directory>./</directory>
</testsuite>
</testsuites>
<!-- Selenium browser set -->
<selenium>
<browser name="Internet Explorer"
browser="*iexplore" />
<browser name="Firefox"
browser="*firefox" />
</selenium>
<!-- Code coverage filter -->
<filter>
<whitelist processUncoveredFiles...
...FromWhitelist="false">
<directory suffix=".php">
../../../hako/classes
</directory>
</whitelist>
</filter>
<!-- Code Coverage report -->
<logging>
<log type="coverage-html"
target="./report"
charset="UTF-8"
highlight="false"
lowUpperBound="35"
highLowerBound="70"/>
</logging>
</phpunit>
phpunit.xml
● backupStaticAttributes="true"
Przełącznik te pozwala za zachowanie zmiennych statycznych
pomiędzy testami. Zmienne globalne są zachowywane
domyślnie.
● bootstrap="bootstrap.php"
Wskazuje położenie pliku inicjującego testy
● <filter>
Pozwala na odfiltrowanie plików źródłowych dla generowanych
raportów pokrycia kodu.
Jak działa PHPUnit?
● bootstrap.php → ładuje klase TestTest → setUpBeforeClass()
→ setUp() → testTest1() → tearDown() → setUp() →
testTest2() → tearDown() → … → tearDownAfterClass()
● Automatycznie konwertuje błędy do wyjątków które są potem
wyświetlane w opisie pod testem.
● Automatycznie traktuje klasy z suffiksem Test jak test
jednostkowy
● Wykonuje metody z prefiksem test lub oznaczone
komentarzem @test.
● Metody setUp() oraz tearDown() służa do „sprzątania” przed i
po testach.
Testy weryfikujemy porównaniami
self::assertEquals() // ==
self::assertSame() // === lepsze przy testowaniu tablic
self::assertEmpty()
self::assertContains()
self::assertCount()
self::assertTrue()
self::assertRegExp()
self::assertFileExists()
self::assertJsonStringEqualsJsonFile()
self::fail($message)
● W wersji 3.7 self:: → $this->
● I wiele innych...
Prosty przykład
<?php
namespace tests;
class ValidTest extends Kohana_UnitTest_TestCase
{
/**
* @covers Valid::min_value
*/
public function test_min_value()
{
$actual = Valid::min_value(33, 40);
$this->assertEquals(40, $actual);
}
}
@dataProvider
public function
dataTestMinValue()
{
return array(
array(0, 0, true),
array(0, 1, false),
array('23', 0, true),
array('sdfdsf', 0, false),
);
}
/**
* @dataProvider dataTestMinValue
* @covers Valid::min_value
*/
public function test_min_value
($value, $limit, $excepted)
{
$actual = Valid::min_value($value,
$limit);
$this->assertEquals($excepted
$actual);
}
● Testują funkcję zdarza się ze powielamy testy
różnice się tylko o parametry, definiujące provider
danych możemy temu zapobiec.
Przechwytywanie wyjątków
/**
* @covers Valid::min_value
* @expectedException ExceptionClass
* @expectedExceptionCode 123
* @expectedExceptionMessage Tekst wyj tkuą
*/
public function test_min_value
($value, $limit, $excepted)
{
functionUnderTest('should throw exception');
}
● @expectedExceptionCode oraz @expectedExceptionMessage są
opcjonalne.
● Dla @expectedException domyślna klasa to Exception.
Testy jednostkowe
● Powinny działać ultra szybko.
● Weryfikują działanie cząstki testowanego kodu
● Skupiamy się jedynie na testowanej metodzie /
funkcji. Testowany kod powinien działać nawet gdy
zależny on od komponentu którego jeszcze nie ma
→ wszelkie powiązania powinniśmy zastępować
mockami.
● Traktujemy testy jak użytkownika pisanego przez
nas api. Powinniśmy formować kod tak by był łatwy
do przetestowania.
Testy jednostkowe
function abc($a, $b)
{
$c = $a;
if ($a > 0)
{
$c += $b;
if ($b < 0)
{
$c *= $b;
}
}
return $c;
}
public function testAbc1()
{
$this->assertEquals(-1, -1);
}
public function testAbc2()
{
$this->assertEquals(1, -1);
}
public function testAbc3()
{
$this->assertEquals(1, 1);
}
● Testujemy pojedynczą metodę / funkcję
Dokładniej mówią testujemy pojedyncza ścieżkę
wykonywania się tak by pokrycie kody wyniosło 100%.
● Nie testujemy frameworka, nie powinniśmy
testować tej samej funkcjonalności w kilku
miejscach.
Co oznacz że kod jest łatwy do
przetestowania?
● Zależności można łatwo zastąpić makietami
obiektów (Dependancy Injection)
● Złożoność cyklomatyczna jest niska. Tzn. jest
stosunkowo niedużo ifów, forów itp., a ich
zagnieżdżenia nie przekraczają głębokość ok. 5.
● Metoda nie powinna być nadmiernie długa (ok. 200
linijek).
● Cyklomatyczność oraz duża ilość metod prywatnych
może sugerować utworzenie nowej klasy.
Dependency Injection
● Największym wrogiem testów jest tzw.
Hardcodeded dependency, czyli zależność której nie
możemy w łatwy sposób zastąpić makietą.
public function login()
{
$login = $this->param('login');
$pass = $this->param('pass');
if ($this->authenticate($login, $pass)) {
$this->redirect('/');
}
else {
$mail = new Mail();
$mail->subject = …;
$mail->to = …;
$mail->body = …;
$mail->send();
}
}
● Jak zastąpić $mail = new Mail();?
Dependency Injection
● By kod przetestować należy go odpowiednio
zmodyfikować, szczególnie w przypadku gdy kod
został stworzony przed napisaniem testów.
● Popularnymi sposobami na wstrzykiwanie
zależności są:
- Constructor injection (przez konstruktor)
- Property injection (dodatkowa właściwość obiektu)
- Factory method (metoda generująca obiekty)
- Isolation of Control Container
Makiety obiektów
● Stubs
Symulują / udają działanie
obiektów.
$stub = $this->getMock('Mail');
$stub->expects($this->any())
->method('send')
->will(
$this->returnValue(true)
);
● Mocks - Weryfikują czy
prawidłowo korzystamy z
obiektów.
$mock = $this->getMock('Mail');
$mock->expects($this->once())
->method('addTo')
->with(
$this->equalTo('m@t.c')
);
Makiety obiektów
● W większość frameworków stuby i mocki są w
rzeczywistości tym samym obiektem.
● W praktyce częściej stosujemy stuby, ale zdarzają
się mocki hybrydowe, czasami mockujemy obiekt
które testujemy.
● Makiety obiektów pozwalają nam zasymulowac
dowolną sytuację w badanym kodzie.
Makiety obiektów
● Parametry dla expects()
self::any()
self::never()
self::atLeastOnce()
self::once()
self::exactly($count)
self::at($index)
● Jeżeli któryś z warunków nie zostanie spełniony test
zostanie oznaczony jako nieudany.
Makiety obiektów
● with() - Akceptuje dowolną listę argumentów:
self::anything()
self::contains($value)
self::arrayHasKey($key)
self::equalTo($value, $delta, $maxDepth)
self::classHasAttribute($attribute)
self::greaterThan($value)
self::isInstanceOf($className)
self::isType($type)
self::matchesRegularExpression($regex)
self::stringContains($string, $case)
● withAnyParameters() → cokolwiek
● Niespełnienie warunków zfailuje test.
Makiety obiektów
● will() → wartości zwracana przez metodę
self::returnValue($value)
self::returnArgument($argumentIndex)
self::returnCallback($stub)
self::returnSelf()
self::returnValueMap($valueMap)
self::throwException($exception)
self::onConsecutiveCalls(...)
Makiety obiektów
namespace tests;
class ClassToMock
{
public function method($arg)
{
throw new Exception('Original method invoked');
}
}
class MockTest extends PHPUnit_Framework_TestCase
{
public function testMock()
{
$stubMock = $this->getMock('testsClassToMock');
$stubMock->expects($this->at(0))
->method('method')
->with(self::equalTo('getMe33'))
->will(self::returnValue(33));
$stubMock->expects($this->at(1))
->method('method')
->with(self::equalTo('getMe44'))
->will(self::returnValue(44));
self::assertSame(33, $stubMock->send('getMe33'));
self::assertSame(44, $stubMock->send('getMe44'));
}
}
Makiety obiektów
● XpMock → warrper dla PHPUnit mocks
$this->mock('MyClass')
->getBool(true)
->getNumber(1)
->getString('string')
->new();
● Wymaga 5.4 (działa jako trait)
● https://github.com/ptrofimov/xpmock
Makiety globalnych obiektów i funkcji
//application
namespace app
{
class TestObject
{
public function method()
{
$model = ORM::factory('Test');
file_exists('some file');
}
}
}
//tests
namespace app
{
class ORM
{
static public function
factory($className, $id=null)
{
echo "my ORM::factoryn";
return new stdClass;
}
}
}
//tests
namespace app
{
function file_exists($filename)
{
echo "my file_existsn";
return file_exists($filename);
}
}
namespace test
{
class TestTest extends
PHPUnit_Framework_TestCase
{
public function testTest()
{
$obj = new appTestObject;
$obj->method();
}
}
}
Reflection API
● Gdy piszemy dla testy dla cudzego kodu, po fakcie,
bardzo przydatnym narzędziem jest Reflection API.
Pozwala ono dostac się do niedostepnych
zakamarków kodu.
namespace tests;
class SomeClass
{
private function prvMethod($arg1) {
return $arg1;
}
}
class PrvTest extends PHPUnit_Framework_TestCase
{
public function testPrivateMethod() {
$obj = new testsSomeClass();
$class = new ReflectionClass($obj);
$method = $class->getMethod('prvMethod');
$method->setAccessible(true);
$method->invoke($obj, 'arg1');
}
}
TDD
● Test Driven Development
● Polega na stworzeniu testów przed przystąpieniem
do kodowania (praca red to green)
● Podejście to pozwala na tworzenie lepszych testów
● Mitem jest przeświadczenie że należy posiadać
dokładne założenia projektowe by go zastosować
● TDD można stosować do pojedynczych tasków.
Code Coverage
● Gotowe narzędzie do analizowania pokrycia testów
jednostkowych.
● Dobrze jest stosować tag @covers. Raport będzie
brał pod uwagę tylko kod do którego stworzyliśmy
testy intencjonalnie.
● Wymaga zainstalowanego Xdebug 2.1.3 (nie
instaluj 2.2.4 bo nie działa, najlepiej 2.2.3).
● If ($a == 0) $c= 3 else $d = 5
traktowane jest jako jedno wyrażenie dlatego
wymaganie jest stosowanie klamer.
Code Coverage
● Dodatkowo raport zawiera metrykę kodu
CRAP. Jeżeli osiągnęliśmy 100% a
metryka >= 100 powinniśmy
refaktoryzować program.
● phpunit --coverage-html ./report
● phpunit.xml
<filter>
<whitelist processUncovered...
...FilesFromWhitelist="false">
<directory suffix=".php">
../../../hako/classes
</directory>
</whitelist>
</filter>
<logging>
<log type="coverage-html"
target="./report" charset="UTF-8"
highlight="false" lowUpperBound="35"
highLowerBound="70"/>
</logging>
Testy integracyjne
● Weryfikacja poprawności komunikacji między aplikacją
a zewnętrznymi zasobami (gł. baza danych).
● Powinniśmy jedynie sprawdzać czy obiekty się
poprawnie wstawiają / usuwają / pobierają. Nie
powinniśmy mieszać z logiką biznesową (od tego są
unit testy).
● Wymaga utworzenia zbioru testowego który należy
odbudowywać przed wykonaniem każdego z testów.
Gdy framework korzysta z PDO można wykorzystać
hack z zagnieżdżonymi transakcjami
https://github.com/wakeless/transaction_pdo/blob/master/TransactionPDO.php
Testy integracyjne
● Przykładem dobrego ORM (pod kątem testów
jednostkowych) jest ten z Zend Framework.
● ORM oparte o ActiveRecord są trudne do testowania.
Powodem jest powiązanie obiektu biznesowego z
reprezentacją w bazie danych (Kohana, Yii).
● W aplikacji nie powinniśmy stosować statycznych
zapytań SQL, a korzystać z tego co oferuje framework.
Testy funkcjonalne (e2e)
● Pozwalają testować wymagania użytkownika
class TestFunctional extends PHPUnit_Extensions_SeleniumTestCase
{
const SITE_URL = 'http://beta.modeview.dev/';
protected function setUp()
{
$this->setBrowser('*firefox');
$this->setBrowserUrl(self::SITE_URL);
}
public function testTitle()
{
$this->open(self::SITE_URL);
$this->assertElementPresent('div.languages');
}
}
● Wymaga uruchomienia servera selenium
java -jar selenium-server-standalone.jar
Testowanie MVC
● Nie ma potrzeby tworzenia testów e2e by
zweryfikować działanie akcji.
● Przykładem może być Zend Framework → można
mockować obiekt request / response i badać efekt
działania akcji poszczególnych kontrolerów (ta
sama zasada dotyczy metod typu before(),
after()).
● Źródłem wielu błędów są widoki. Mają one ustalone
parametry przekazywane z kontrolera, możemy więc
generować ich rożne wartości i przekazywać do
widoków.
Do przeczytania
● http://phpunit.de
● http://artofunittesting.com/
● xUnit Design Patterns
● PHP Reflection API
● Art of Unit Testing [Part 2]
● https://github.com/ptrofimov/xpmock

More Related Content

What's hot

[COSCUP 2022] 讓黑畫面再次偉大 - 用 PHP 寫 CLI 工具
[COSCUP 2022] 讓黑畫面再次偉大 - 用 PHP 寫 CLI 工具[COSCUP 2022] 讓黑畫面再次偉大 - 用 PHP 寫 CLI 工具
[COSCUP 2022] 讓黑畫面再次偉大 - 用 PHP 寫 CLI 工具Shengyou Fan
 
Angular 7 Firebase5 CRUD Operations with Reactive Forms
Angular 7 Firebase5 CRUD Operations with Reactive FormsAngular 7 Firebase5 CRUD Operations with Reactive Forms
Angular 7 Firebase5 CRUD Operations with Reactive FormsDigamber Singh
 
Domain Modeling Made Functional (DevTernity 2022)
Domain Modeling Made Functional (DevTernity 2022)Domain Modeling Made Functional (DevTernity 2022)
Domain Modeling Made Functional (DevTernity 2022)Scott Wlaschin
 
Naming Standards, Clean Code
Naming Standards, Clean CodeNaming Standards, Clean Code
Naming Standards, Clean CodeCleanestCode
 
영속성 컨텍스트로 보는 JPA
영속성 컨텍스트로 보는 JPA영속성 컨텍스트로 보는 JPA
영속성 컨텍스트로 보는 JPA경원 이
 
第三回ありえる社内勉強会 「いわががのLombok」
第三回ありえる社内勉強会 「いわががのLombok」第三回ありえる社内勉強会 「いわががのLombok」
第三回ありえる社内勉強会 「いわががのLombok」yoshiaki iwanaga
 
Pipeline oriented programming
Pipeline oriented programmingPipeline oriented programming
Pipeline oriented programmingScott Wlaschin
 
PHP Functions & Arrays
PHP Functions & ArraysPHP Functions & Arrays
PHP Functions & ArraysHenry Osborne
 
「多要素認証」と言っても色々あるんです
「多要素認証」と言っても色々あるんです「多要素認証」と言っても色々あるんです
「多要素認証」と言っても色々あるんですIIJ
 
Array assignment
Array assignmentArray assignment
Array assignmentAhmad Kamal
 
computer project code ''payroll'' (based on datafile handling)
computer project code ''payroll'' (based on datafile handling)computer project code ''payroll'' (based on datafile handling)
computer project code ''payroll'' (based on datafile handling)Nitish Yadav
 
Java script final presentation
Java script final presentationJava script final presentation
Java script final presentationAdhoura Academy
 
Implementing a decorator for thread synchronisation.
Implementing a decorator for thread synchronisation.Implementing a decorator for thread synchronisation.
Implementing a decorator for thread synchronisation.Graham Dumpleton
 
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...Chris Richardson
 
Domain Driven Design with the F# type System -- NDC London 2013
Domain Driven Design with the F# type System -- NDC London 2013Domain Driven Design with the F# type System -- NDC London 2013
Domain Driven Design with the F# type System -- NDC London 2013Scott Wlaschin
 

What's hot (20)

Flask Basics
Flask BasicsFlask Basics
Flask Basics
 
[COSCUP 2022] 讓黑畫面再次偉大 - 用 PHP 寫 CLI 工具
[COSCUP 2022] 讓黑畫面再次偉大 - 用 PHP 寫 CLI 工具[COSCUP 2022] 讓黑畫面再次偉大 - 用 PHP 寫 CLI 工具
[COSCUP 2022] 讓黑畫面再次偉大 - 用 PHP 寫 CLI 工具
 
Angular 7 Firebase5 CRUD Operations with Reactive Forms
Angular 7 Firebase5 CRUD Operations with Reactive FormsAngular 7 Firebase5 CRUD Operations with Reactive Forms
Angular 7 Firebase5 CRUD Operations with Reactive Forms
 
Domain Modeling Made Functional (DevTernity 2022)
Domain Modeling Made Functional (DevTernity 2022)Domain Modeling Made Functional (DevTernity 2022)
Domain Modeling Made Functional (DevTernity 2022)
 
Writing clean code
Writing clean codeWriting clean code
Writing clean code
 
Naming Standards, Clean Code
Naming Standards, Clean CodeNaming Standards, Clean Code
Naming Standards, Clean Code
 
영속성 컨텍스트로 보는 JPA
영속성 컨텍스트로 보는 JPA영속성 컨텍스트로 보는 JPA
영속성 컨텍스트로 보는 JPA
 
第三回ありえる社内勉強会 「いわががのLombok」
第三回ありえる社内勉強会 「いわががのLombok」第三回ありえる社内勉強会 「いわががのLombok」
第三回ありえる社内勉強会 「いわががのLombok」
 
Pipeline oriented programming
Pipeline oriented programmingPipeline oriented programming
Pipeline oriented programming
 
Python programming : Inheritance and polymorphism
Python programming : Inheritance and polymorphismPython programming : Inheritance and polymorphism
Python programming : Inheritance and polymorphism
 
PHP Functions & Arrays
PHP Functions & ArraysPHP Functions & Arrays
PHP Functions & Arrays
 
behatで始めるBDD
behatで始めるBDDbehatで始めるBDD
behatで始めるBDD
 
「多要素認証」と言っても色々あるんです
「多要素認証」と言っても色々あるんです「多要素認証」と言っても色々あるんです
「多要素認証」と言っても色々あるんです
 
Array assignment
Array assignmentArray assignment
Array assignment
 
computer project code ''payroll'' (based on datafile handling)
computer project code ''payroll'' (based on datafile handling)computer project code ''payroll'' (based on datafile handling)
computer project code ''payroll'' (based on datafile handling)
 
Java script final presentation
Java script final presentationJava script final presentation
Java script final presentation
 
Implementing a decorator for thread synchronisation.
Implementing a decorator for thread synchronisation.Implementing a decorator for thread synchronisation.
Implementing a decorator for thread synchronisation.
 
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
 
Domain Driven Design with the F# type System -- NDC London 2013
Domain Driven Design with the F# type System -- NDC London 2013Domain Driven Design with the F# type System -- NDC London 2013
Domain Driven Design with the F# type System -- NDC London 2013
 
Clean code
Clean codeClean code
Clean code
 

Similar to Wprowadzenie do PHPUnit

Poznańska grupa .Net spotkanie VI - Test Driven Development
Poznańska grupa .Net spotkanie VI - Test Driven DevelopmentPoznańska grupa .Net spotkanie VI - Test Driven Development
Poznańska grupa .Net spotkanie VI - Test Driven Developmentbartlomiej.szafko
 
Jak zacząć, aby nie żałować - czyli 50 twarzy PHP
Jak zacząć, aby nie żałować - czyli 50 twarzy PHPJak zacząć, aby nie żałować - czyli 50 twarzy PHP
Jak zacząć, aby nie żałować - czyli 50 twarzy PHPPiotr Horzycki
 
TGT#14 - @Before – Nie będę automatyzować @After – No dobra, to nie jest taki...
TGT#14 - @Before – Nie będę automatyzować @After – No dobra, to nie jest taki...TGT#14 - @Before – Nie będę automatyzować @After – No dobra, to nie jest taki...
TGT#14 - @Before – Nie będę automatyzować @After – No dobra, to nie jest taki...Trójmiejska Grupa Testerska
 
Testy jednostkowe - PHPUnit
Testy jednostkowe - PHPUnitTesty jednostkowe - PHPUnit
Testy jednostkowe - PHPUnitPHPstokPHPstok
 
Jak stworzyć udany system informatyczny
Jak stworzyć udany system informatycznyJak stworzyć udany system informatyczny
Jak stworzyć udany system informatycznyqbeuek
 
Podstawy testowania oprogramowania INCO 2023.pptx
Podstawy testowania oprogramowania INCO 2023.pptxPodstawy testowania oprogramowania INCO 2023.pptx
Podstawy testowania oprogramowania INCO 2023.pptxKatarzyna Javaheri-Szpak
 
Xdebug – debugowanie i profilowanie aplikacji PHP
Xdebug – debugowanie i profilowanie aplikacji PHPXdebug – debugowanie i profilowanie aplikacji PHP
Xdebug – debugowanie i profilowanie aplikacji PHP3camp
 
PLNOG19 - Andrzej Prałat & Wojciech Rybicki - Jak usprawnić działanie WAF wyk...
PLNOG19 - Andrzej Prałat & Wojciech Rybicki - Jak usprawnić działanie WAF wyk...PLNOG19 - Andrzej Prałat & Wojciech Rybicki - Jak usprawnić działanie WAF wyk...
PLNOG19 - Andrzej Prałat & Wojciech Rybicki - Jak usprawnić działanie WAF wyk...PROIDEA
 
Strategie automatyzacji testow
Strategie automatyzacji testowStrategie automatyzacji testow
Strategie automatyzacji testowWiktor Żołnowski
 
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)Codesushi.co (CODESUSHI LLC)
 
Zwiększanie produktywności programisty php (v2)
Zwiększanie produktywności programisty php (v2)Zwiększanie produktywności programisty php (v2)
Zwiększanie produktywności programisty php (v2)adamhmetal
 
Zwiększanie produktywności programisty php
Zwiększanie produktywności programisty phpZwiększanie produktywności programisty php
Zwiększanie produktywności programisty phpadamhmetal
 
Automatyzacja utrzymania jakości w środowisku PHP
Automatyzacja utrzymania jakości w środowisku PHPAutomatyzacja utrzymania jakości w środowisku PHP
Automatyzacja utrzymania jakości w środowisku PHPLaravel Poland MeetUp
 
xD bug - Jak debugować PHP-owe aplikacje (Xdebug)
xD bug - Jak debugować PHP-owe aplikacje (Xdebug) xD bug - Jak debugować PHP-owe aplikacje (Xdebug)
xD bug - Jak debugować PHP-owe aplikacje (Xdebug) Laravel Poland MeetUp
 
KraQA #22, Filip Cynarski - Selenium Grid w chmurze Amazon Web Services
KraQA #22, Filip Cynarski -  Selenium Grid w chmurze Amazon Web ServicesKraQA #22, Filip Cynarski -  Selenium Grid w chmurze Amazon Web Services
KraQA #22, Filip Cynarski - Selenium Grid w chmurze Amazon Web Serviceskraqa
 
Daj się wyręczyć - Joomla Day Polska 2014
Daj się wyręczyć - Joomla Day Polska 2014Daj się wyręczyć - Joomla Day Polska 2014
Daj się wyręczyć - Joomla Day Polska 2014Tomasz Dziuda
 
Integracja środowiska testowego z użyciem Robot Framework, TrojQA 2014-12-16
Integracja środowiska testowego z użyciem Robot Framework, TrojQA 2014-12-16Integracja środowiska testowego z użyciem Robot Framework, TrojQA 2014-12-16
Integracja środowiska testowego z użyciem Robot Framework, TrojQA 2014-12-16Krzysztof Synak
 

Similar to Wprowadzenie do PHPUnit (20)

Poznańska grupa .Net spotkanie VI - Test Driven Development
Poznańska grupa .Net spotkanie VI - Test Driven DevelopmentPoznańska grupa .Net spotkanie VI - Test Driven Development
Poznańska grupa .Net spotkanie VI - Test Driven Development
 
Jak zacząć, aby nie żałować - czyli 50 twarzy PHP
Jak zacząć, aby nie żałować - czyli 50 twarzy PHPJak zacząć, aby nie żałować - czyli 50 twarzy PHP
Jak zacząć, aby nie żałować - czyli 50 twarzy PHP
 
TGT#14 - @Before – Nie będę automatyzować @After – No dobra, to nie jest taki...
TGT#14 - @Before – Nie będę automatyzować @After – No dobra, to nie jest taki...TGT#14 - @Before – Nie będę automatyzować @After – No dobra, to nie jest taki...
TGT#14 - @Before – Nie będę automatyzować @After – No dobra, to nie jest taki...
 
Testy jednostkowe - PHPUnit
Testy jednostkowe - PHPUnitTesty jednostkowe - PHPUnit
Testy jednostkowe - PHPUnit
 
Testowanie automatyczne 2024 INCO Academy
Testowanie automatyczne 2024 INCO AcademyTestowanie automatyczne 2024 INCO Academy
Testowanie automatyczne 2024 INCO Academy
 
Jak stworzyć udany system informatyczny
Jak stworzyć udany system informatycznyJak stworzyć udany system informatyczny
Jak stworzyć udany system informatyczny
 
Podstawy testowania oprogramowania INCO 2023.pptx
Podstawy testowania oprogramowania INCO 2023.pptxPodstawy testowania oprogramowania INCO 2023.pptx
Podstawy testowania oprogramowania INCO 2023.pptx
 
Testowanie na 101 sposobów
Testowanie na 101 sposobówTestowanie na 101 sposobów
Testowanie na 101 sposobów
 
Xdebug – debugowanie i profilowanie aplikacji PHP
Xdebug – debugowanie i profilowanie aplikacji PHPXdebug – debugowanie i profilowanie aplikacji PHP
Xdebug – debugowanie i profilowanie aplikacji PHP
 
PLNOG19 - Andrzej Prałat & Wojciech Rybicki - Jak usprawnić działanie WAF wyk...
PLNOG19 - Andrzej Prałat & Wojciech Rybicki - Jak usprawnić działanie WAF wyk...PLNOG19 - Andrzej Prałat & Wojciech Rybicki - Jak usprawnić działanie WAF wyk...
PLNOG19 - Andrzej Prałat & Wojciech Rybicki - Jak usprawnić działanie WAF wyk...
 
university day 1
university day 1university day 1
university day 1
 
Strategie automatyzacji testow
Strategie automatyzacji testowStrategie automatyzacji testow
Strategie automatyzacji testow
 
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
 
Zwiększanie produktywności programisty php (v2)
Zwiększanie produktywności programisty php (v2)Zwiększanie produktywności programisty php (v2)
Zwiększanie produktywności programisty php (v2)
 
Zwiększanie produktywności programisty php
Zwiększanie produktywności programisty phpZwiększanie produktywności programisty php
Zwiększanie produktywności programisty php
 
Automatyzacja utrzymania jakości w środowisku PHP
Automatyzacja utrzymania jakości w środowisku PHPAutomatyzacja utrzymania jakości w środowisku PHP
Automatyzacja utrzymania jakości w środowisku PHP
 
xD bug - Jak debugować PHP-owe aplikacje (Xdebug)
xD bug - Jak debugować PHP-owe aplikacje (Xdebug) xD bug - Jak debugować PHP-owe aplikacje (Xdebug)
xD bug - Jak debugować PHP-owe aplikacje (Xdebug)
 
KraQA #22, Filip Cynarski - Selenium Grid w chmurze Amazon Web Services
KraQA #22, Filip Cynarski -  Selenium Grid w chmurze Amazon Web ServicesKraQA #22, Filip Cynarski -  Selenium Grid w chmurze Amazon Web Services
KraQA #22, Filip Cynarski - Selenium Grid w chmurze Amazon Web Services
 
Daj się wyręczyć - Joomla Day Polska 2014
Daj się wyręczyć - Joomla Day Polska 2014Daj się wyręczyć - Joomla Day Polska 2014
Daj się wyręczyć - Joomla Day Polska 2014
 
Integracja środowiska testowego z użyciem Robot Framework, TrojQA 2014-12-16
Integracja środowiska testowego z użyciem Robot Framework, TrojQA 2014-12-16Integracja środowiska testowego z użyciem Robot Framework, TrojQA 2014-12-16
Integracja środowiska testowego z użyciem Robot Framework, TrojQA 2014-12-16
 

Wprowadzenie do PHPUnit

  • 1. Wprowadzenie do PHPUnit By Michał Kowalik
  • 2. Czym są testy jednostkowe? ● W programowaniu metoda testowania tworzonego oprogramowania poprzez wykonywanie testów weryfikujących poprawność działania pojedynczych elementów (jednostek) programu. ● Wyróżniamy następujące rodzaje: - testy jednostkowe weryfikacja kodu - testy integracyjne weryfikacja komunikacji z zasobami np. bazą danych - testy funkcjonalne /end-to-end/ weryfikacja wymagań użytkownika
  • 3. Korzyści ● Są automatyczne Odbywają się za nas, nie musimy pamiętać by ręcznie sprawdzić jakiś tam jeszcze edge-case. ● Bardzo dobrze wpływają na jakość kodu Dzielimy program na mniejsze klocki. Zaczynamy korzystać z wzorców projektowych. Za każdym razem musimy odpowiedzieć sobie na pytanie Jak ja to potem przetestuje?
  • 4. Korzyści ● Pozwalają wykrywać problemy na etapie tworzenia aplikacji. ● Skracają czas programowania. Nie musimy przeskakiwać do przeglądarki i tracić czasu na ręczne testy. Możemy pracować bez odrywania się od IDE.
  • 5. Korzyści ● PHP jest dynamicznym językiem Wykrywanie błędów składni, nieistniejących metod, niewłaściwego wykorzystania typów, błędnego wykorzystania funkcji wbudowanych ● Weryfikacja działania na różnych platformach Programiści mogą pracować na Windowsach, ale serwery są zazwyczaj na Linuxach Co pewien czas wychodzi nowa wersja PHP, testy pozwalają nam sprawdzić czy program zadziała w nowym środowisku (np. w HHVM) Obecność testów jest konieczna do wykorzystania narzędzi typu Continous Integration (Github / Travis).
  • 6. Korzyści ● Testy nabierają znaczenia gdy nasz projekt rośnie Początkowo testy mogą wydawać się zbędę i niepotrzebnie nas obciążać W marę jak rozbudowujemy projekt testy pozwalają zweryfikować czy zmiany nie uszkodziły przedniej funkcjonaliści Pozwalają nowemu programiści w zespole sprawdzić – samodzielnie – czy czegoś nie zepsuł.
  • 7. Korzyści tylko wtedy gdy: ● Testy działają szybko ● Nie generują dodatkowych błędów Są napisane w możliwie prosty sposób ● Są w stanie przetrwać ew. modyfikacje kodu Testy nie mogą być zbyt szczegółowe ● Generalnie pisanie testów jest sztuką samą w sobie Oddzielna specjalizacja
  • 8. Instalacja (phar) - zalecana #unix wget https://phar.phpunit.de/phpunit.phar chmod +x phpunit.phar mv phpunit.phar phpunit #phpunit.bat c:phpphp.exe c:phpphpunit.phar %* #sprawdzenie poprawności > phpunit –-version PHPunit 4.1.0 by Sebastian Bergmann.
  • 9. Instalacja 3.7 (pear) - przestarzała pear update-channels pear config-set auto_discover 1 pear channel-discover pear.phpunit.de pear install --alldeps --force phpunit/PHPUnit pear install --alldeps --force phpunit/DbUnit pear install --alldeps --force phpunit/PHPUnit_Selenium pear install --alldeps --force phpunit/PHPUnit_SkeletonGenerator pear install --alldeps --force phpunit/PHPUnit_Story pear install --alldeps --force phpunit/PHP_CodeCoverage pear install --alldeps --force phpunit/PHP_Invoker
  • 10. Konfiguracja ● phpunit.xml Dzięki niemu możemy skonfigurować środowisko w jednym miejscu i uruchomić testy poprzez ./phpunit ● bootsrap.php W tym piku inicjujemy projekt (ustawiamy globalne zmienne, class loadery, startujemy framework). Bardzo często zawiera prawie to samo co public/index.php Położenie można ustalić w phpunit.xml
  • 11. phpunit.xml <?xml version="1.0" encoding="UTF-8"?> <phpunit backupGlobals="true" backupStaticAttributes="true" bootstrap="bootstrap.php" cacheTokens="false" colors="false" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnError="false" stopOnFailure="false" stopOnIncomplete="false" stopOnSkipped="false" timeoutForSmallTests="1" timeoutForMediumTests="10" timeoutForLargeTests="60" strict="false" verbose="false" > <testsuites> <testsuite name="Kohana Tests"> <directory>./</directory> </testsuite> </testsuites> <!-- Selenium browser set --> <selenium> <browser name="Internet Explorer" browser="*iexplore" /> <browser name="Firefox" browser="*firefox" /> </selenium> <!-- Code coverage filter --> <filter> <whitelist processUncoveredFiles... ...FromWhitelist="false"> <directory suffix=".php"> ../../../hako/classes </directory> </whitelist> </filter> <!-- Code Coverage report --> <logging> <log type="coverage-html" target="./report" charset="UTF-8" highlight="false" lowUpperBound="35" highLowerBound="70"/> </logging> </phpunit>
  • 12. phpunit.xml ● backupStaticAttributes="true" Przełącznik te pozwala za zachowanie zmiennych statycznych pomiędzy testami. Zmienne globalne są zachowywane domyślnie. ● bootstrap="bootstrap.php" Wskazuje położenie pliku inicjującego testy ● <filter> Pozwala na odfiltrowanie plików źródłowych dla generowanych raportów pokrycia kodu.
  • 13. Jak działa PHPUnit? ● bootstrap.php → ładuje klase TestTest → setUpBeforeClass() → setUp() → testTest1() → tearDown() → setUp() → testTest2() → tearDown() → … → tearDownAfterClass() ● Automatycznie konwertuje błędy do wyjątków które są potem wyświetlane w opisie pod testem. ● Automatycznie traktuje klasy z suffiksem Test jak test jednostkowy ● Wykonuje metody z prefiksem test lub oznaczone komentarzem @test. ● Metody setUp() oraz tearDown() służa do „sprzątania” przed i po testach.
  • 14. Testy weryfikujemy porównaniami self::assertEquals() // == self::assertSame() // === lepsze przy testowaniu tablic self::assertEmpty() self::assertContains() self::assertCount() self::assertTrue() self::assertRegExp() self::assertFileExists() self::assertJsonStringEqualsJsonFile() self::fail($message) ● W wersji 3.7 self:: → $this-> ● I wiele innych...
  • 15. Prosty przykład <?php namespace tests; class ValidTest extends Kohana_UnitTest_TestCase { /** * @covers Valid::min_value */ public function test_min_value() { $actual = Valid::min_value(33, 40); $this->assertEquals(40, $actual); } }
  • 16. @dataProvider public function dataTestMinValue() { return array( array(0, 0, true), array(0, 1, false), array('23', 0, true), array('sdfdsf', 0, false), ); } /** * @dataProvider dataTestMinValue * @covers Valid::min_value */ public function test_min_value ($value, $limit, $excepted) { $actual = Valid::min_value($value, $limit); $this->assertEquals($excepted $actual); } ● Testują funkcję zdarza się ze powielamy testy różnice się tylko o parametry, definiujące provider danych możemy temu zapobiec.
  • 17. Przechwytywanie wyjątków /** * @covers Valid::min_value * @expectedException ExceptionClass * @expectedExceptionCode 123 * @expectedExceptionMessage Tekst wyj tkuą */ public function test_min_value ($value, $limit, $excepted) { functionUnderTest('should throw exception'); } ● @expectedExceptionCode oraz @expectedExceptionMessage są opcjonalne. ● Dla @expectedException domyślna klasa to Exception.
  • 18. Testy jednostkowe ● Powinny działać ultra szybko. ● Weryfikują działanie cząstki testowanego kodu ● Skupiamy się jedynie na testowanej metodzie / funkcji. Testowany kod powinien działać nawet gdy zależny on od komponentu którego jeszcze nie ma → wszelkie powiązania powinniśmy zastępować mockami. ● Traktujemy testy jak użytkownika pisanego przez nas api. Powinniśmy formować kod tak by był łatwy do przetestowania.
  • 19. Testy jednostkowe function abc($a, $b) { $c = $a; if ($a > 0) { $c += $b; if ($b < 0) { $c *= $b; } } return $c; } public function testAbc1() { $this->assertEquals(-1, -1); } public function testAbc2() { $this->assertEquals(1, -1); } public function testAbc3() { $this->assertEquals(1, 1); } ● Testujemy pojedynczą metodę / funkcję Dokładniej mówią testujemy pojedyncza ścieżkę wykonywania się tak by pokrycie kody wyniosło 100%. ● Nie testujemy frameworka, nie powinniśmy testować tej samej funkcjonalności w kilku miejscach.
  • 20. Co oznacz że kod jest łatwy do przetestowania? ● Zależności można łatwo zastąpić makietami obiektów (Dependancy Injection) ● Złożoność cyklomatyczna jest niska. Tzn. jest stosunkowo niedużo ifów, forów itp., a ich zagnieżdżenia nie przekraczają głębokość ok. 5. ● Metoda nie powinna być nadmiernie długa (ok. 200 linijek). ● Cyklomatyczność oraz duża ilość metod prywatnych może sugerować utworzenie nowej klasy.
  • 21. Dependency Injection ● Największym wrogiem testów jest tzw. Hardcodeded dependency, czyli zależność której nie możemy w łatwy sposób zastąpić makietą. public function login() { $login = $this->param('login'); $pass = $this->param('pass'); if ($this->authenticate($login, $pass)) { $this->redirect('/'); } else { $mail = new Mail(); $mail->subject = …; $mail->to = …; $mail->body = …; $mail->send(); } } ● Jak zastąpić $mail = new Mail();?
  • 22. Dependency Injection ● By kod przetestować należy go odpowiednio zmodyfikować, szczególnie w przypadku gdy kod został stworzony przed napisaniem testów. ● Popularnymi sposobami na wstrzykiwanie zależności są: - Constructor injection (przez konstruktor) - Property injection (dodatkowa właściwość obiektu) - Factory method (metoda generująca obiekty) - Isolation of Control Container
  • 23. Makiety obiektów ● Stubs Symulują / udają działanie obiektów. $stub = $this->getMock('Mail'); $stub->expects($this->any()) ->method('send') ->will( $this->returnValue(true) ); ● Mocks - Weryfikują czy prawidłowo korzystamy z obiektów. $mock = $this->getMock('Mail'); $mock->expects($this->once()) ->method('addTo') ->with( $this->equalTo('m@t.c') );
  • 24. Makiety obiektów ● W większość frameworków stuby i mocki są w rzeczywistości tym samym obiektem. ● W praktyce częściej stosujemy stuby, ale zdarzają się mocki hybrydowe, czasami mockujemy obiekt które testujemy. ● Makiety obiektów pozwalają nam zasymulowac dowolną sytuację w badanym kodzie.
  • 25. Makiety obiektów ● Parametry dla expects() self::any() self::never() self::atLeastOnce() self::once() self::exactly($count) self::at($index) ● Jeżeli któryś z warunków nie zostanie spełniony test zostanie oznaczony jako nieudany.
  • 26. Makiety obiektów ● with() - Akceptuje dowolną listę argumentów: self::anything() self::contains($value) self::arrayHasKey($key) self::equalTo($value, $delta, $maxDepth) self::classHasAttribute($attribute) self::greaterThan($value) self::isInstanceOf($className) self::isType($type) self::matchesRegularExpression($regex) self::stringContains($string, $case) ● withAnyParameters() → cokolwiek ● Niespełnienie warunków zfailuje test.
  • 27. Makiety obiektów ● will() → wartości zwracana przez metodę self::returnValue($value) self::returnArgument($argumentIndex) self::returnCallback($stub) self::returnSelf() self::returnValueMap($valueMap) self::throwException($exception) self::onConsecutiveCalls(...)
  • 28. Makiety obiektów namespace tests; class ClassToMock { public function method($arg) { throw new Exception('Original method invoked'); } } class MockTest extends PHPUnit_Framework_TestCase { public function testMock() { $stubMock = $this->getMock('testsClassToMock'); $stubMock->expects($this->at(0)) ->method('method') ->with(self::equalTo('getMe33')) ->will(self::returnValue(33)); $stubMock->expects($this->at(1)) ->method('method') ->with(self::equalTo('getMe44')) ->will(self::returnValue(44)); self::assertSame(33, $stubMock->send('getMe33')); self::assertSame(44, $stubMock->send('getMe44')); } }
  • 29. Makiety obiektów ● XpMock → warrper dla PHPUnit mocks $this->mock('MyClass') ->getBool(true) ->getNumber(1) ->getString('string') ->new(); ● Wymaga 5.4 (działa jako trait) ● https://github.com/ptrofimov/xpmock
  • 30. Makiety globalnych obiektów i funkcji //application namespace app { class TestObject { public function method() { $model = ORM::factory('Test'); file_exists('some file'); } } } //tests namespace app { class ORM { static public function factory($className, $id=null) { echo "my ORM::factoryn"; return new stdClass; } } } //tests namespace app { function file_exists($filename) { echo "my file_existsn"; return file_exists($filename); } } namespace test { class TestTest extends PHPUnit_Framework_TestCase { public function testTest() { $obj = new appTestObject; $obj->method(); } } }
  • 31. Reflection API ● Gdy piszemy dla testy dla cudzego kodu, po fakcie, bardzo przydatnym narzędziem jest Reflection API. Pozwala ono dostac się do niedostepnych zakamarków kodu. namespace tests; class SomeClass { private function prvMethod($arg1) { return $arg1; } } class PrvTest extends PHPUnit_Framework_TestCase { public function testPrivateMethod() { $obj = new testsSomeClass(); $class = new ReflectionClass($obj); $method = $class->getMethod('prvMethod'); $method->setAccessible(true); $method->invoke($obj, 'arg1'); } }
  • 32. TDD ● Test Driven Development ● Polega na stworzeniu testów przed przystąpieniem do kodowania (praca red to green) ● Podejście to pozwala na tworzenie lepszych testów ● Mitem jest przeświadczenie że należy posiadać dokładne założenia projektowe by go zastosować ● TDD można stosować do pojedynczych tasków.
  • 33. Code Coverage ● Gotowe narzędzie do analizowania pokrycia testów jednostkowych. ● Dobrze jest stosować tag @covers. Raport będzie brał pod uwagę tylko kod do którego stworzyliśmy testy intencjonalnie. ● Wymaga zainstalowanego Xdebug 2.1.3 (nie instaluj 2.2.4 bo nie działa, najlepiej 2.2.3). ● If ($a == 0) $c= 3 else $d = 5 traktowane jest jako jedno wyrażenie dlatego wymaganie jest stosowanie klamer.
  • 34. Code Coverage ● Dodatkowo raport zawiera metrykę kodu CRAP. Jeżeli osiągnęliśmy 100% a metryka >= 100 powinniśmy refaktoryzować program. ● phpunit --coverage-html ./report ● phpunit.xml <filter> <whitelist processUncovered... ...FilesFromWhitelist="false"> <directory suffix=".php"> ../../../hako/classes </directory> </whitelist> </filter> <logging> <log type="coverage-html" target="./report" charset="UTF-8" highlight="false" lowUpperBound="35" highLowerBound="70"/> </logging>
  • 35. Testy integracyjne ● Weryfikacja poprawności komunikacji między aplikacją a zewnętrznymi zasobami (gł. baza danych). ● Powinniśmy jedynie sprawdzać czy obiekty się poprawnie wstawiają / usuwają / pobierają. Nie powinniśmy mieszać z logiką biznesową (od tego są unit testy). ● Wymaga utworzenia zbioru testowego który należy odbudowywać przed wykonaniem każdego z testów. Gdy framework korzysta z PDO można wykorzystać hack z zagnieżdżonymi transakcjami https://github.com/wakeless/transaction_pdo/blob/master/TransactionPDO.php
  • 36. Testy integracyjne ● Przykładem dobrego ORM (pod kątem testów jednostkowych) jest ten z Zend Framework. ● ORM oparte o ActiveRecord są trudne do testowania. Powodem jest powiązanie obiektu biznesowego z reprezentacją w bazie danych (Kohana, Yii). ● W aplikacji nie powinniśmy stosować statycznych zapytań SQL, a korzystać z tego co oferuje framework.
  • 37. Testy funkcjonalne (e2e) ● Pozwalają testować wymagania użytkownika class TestFunctional extends PHPUnit_Extensions_SeleniumTestCase { const SITE_URL = 'http://beta.modeview.dev/'; protected function setUp() { $this->setBrowser('*firefox'); $this->setBrowserUrl(self::SITE_URL); } public function testTitle() { $this->open(self::SITE_URL); $this->assertElementPresent('div.languages'); } } ● Wymaga uruchomienia servera selenium java -jar selenium-server-standalone.jar
  • 38. Testowanie MVC ● Nie ma potrzeby tworzenia testów e2e by zweryfikować działanie akcji. ● Przykładem może być Zend Framework → można mockować obiekt request / response i badać efekt działania akcji poszczególnych kontrolerów (ta sama zasada dotyczy metod typu before(), after()). ● Źródłem wielu błędów są widoki. Mają one ustalone parametry przekazywane z kontrolera, możemy więc generować ich rożne wartości i przekazywać do widoków.
  • 39. Do przeczytania ● http://phpunit.de ● http://artofunittesting.com/ ● xUnit Design Patterns ● PHP Reflection API ● Art of Unit Testing [Part 2] ● https://github.com/ptrofimov/xpmock