Unit Testing
Merangkak Menuju Pengembangan Aplikasi
PHP yang Berkualitas dan Tahan Banting
Arthur Purnama
arthur [at] purnama.de
http://arthur.purnama.de
06.04.2011
Arthur Purnama
Diplom Informatiker Hochschule Fulda
Sejak 2004 bekerja sebagai PHP
Programmer
Sejak 2008 bekerja sebagai ITConsultant
divisi Portal Solutions
Saat ini tinggal di Frankfurt am Main, Jerman
Tujuan
Memperkenalkan Unit Testing
Mendorong atau memotivasi penggunaan
Unit Testing
Memperkenalkan Unit Testing Framework
PHPUnit
Rutinitas Test Programer PHP
Berjam-jam mencari bug
Debugger? apaan tuh?
Teman setia: echo, print_r, var_dump
Mati satu tumbuh seribu (bug baru)
Bug Lama Bersemi Kembali
Unit Testing
In computer programming, unit testing is a
method by which individual units of source
code are tested to determine if they are fit for
use.
A unit is the smallest testable part of an
application.
In object-oriented programming a unit is
usually a method.
http://en.wikipedia.org/wiki/Unit_testing
Unit Testing
Dalam pemrograman komputer, unit testing
merupakan suatu metode yang mana satu
unit dalam source code diuji untuk
menunjukkan bahwa code tersebut bekerja
sebagaimana mestinya.
Unit adalah bagian terkecil dari satu aplikasi
yang dapat diuji.
Dalam pemrograman berorientasi obyek, unit
biasanya adalah method.
Contoh
function multiplicate($a, $b){
return $a*$b;
}
echo multiplicate(1, 2) === 2 ? true : false;
echo PHP_EOL;
echo multiplicate(2, 2) === 4 ? true : false;
echo PHP_EOL;
echo multiplicate(3, 2) !== 5 ? true : false;
Kelebihan Unit Testing
Yakin setiap saat bahwa code berfungsi
sebagaimana mestinya.
Yakin setiap kali aplikasi berkembang, code
tetap berfungsi sebagaimana mestinya.
Yakin setiap kali melakukan re-design atau
refakturisasi, code tetap berfungsi
sebagaimana mestinya.
Kelebihan Unit Testing
Menjadi bagian dari dokumentasi code
Mempermudah Integrasi antar komponen
Meningkatkan kualitas code
Unsur Unit Testing
Harus di otomatisasi
Dapat dijalankan dengan sekali klick
Dapat dengan mudah diulang
Mudah diimplementasikan
Setelah ditulis dapat dijalankan kapan saja
Setiap orang dapat menjalankannya
Berjalan dengan cepat
Unit Test != Integration Test
Unit Test
Tidak berinteraksi dengan basis data
Tidak berinteraksi dengan Jaringan
Tidak berinteraksi dengan File System
Tidak membutuhkan konfigurasi khusus untuk
menjalankannya
Terisolasi dari komponen atau kelas konkrit
lainnya
Unit Testing Framework
PHPUnit
Unit Testing Framework
Ditulis oleh Sebastian Bergmann
De facto standard untuk PHP
Banyak didukung oleh Tools dan Framework lain
Contoh Unit Testing Multiplikasi
<?php
//MultiplikasiTest.php
require_once 'multiplikasi.php';
class BelajarUnitTest extends PHPUnit_Framework_TestCase{
}
?>
Contoh Unit Testing Multiplikasi
<?php
//MultiplikasiTest.php
require_once 'multiplikasi.php';
class BelajarUnitTest extends PHPUnit_Framework_TestCase{
//Nama fungsi menggunakan awalan “test“
public function testMultiplicate(){
}
}
?>
Contoh Unit Testing Multiplikasi
<?php
//MultiplikasiTest.php
require_once 'multiplikasi.php';
class BelajarUnitTest extends PHPUnit_Framework_TestCase{
//Nama fungsi menggunakan awalan “test“
public function testMultiplicate(){
$this->assertEquals(2, multiplicate(1, 2));
}
}
?>
Contoh Unit Testing Multiplikasi
$>phpunit BelajarUnitTest MultiplikasiTest.php
PHPUnit 3.5.12 by Sebastian Bergmann.
.
Time: 0 seconds, Memory: 3.00Mb
OK (1 test, 1 assertion)
Contoh Unit Testing Multiplikasi
<?php
//MultiplikasiTest.php
require_once 'multiplikasi.php';
class BelajarUnitTest extends PHPUnit_Framework_TestCase{
public function testMultiplicate(){
//Sangat tidak dianjurkan. Pastikan hanya selalu menguji SATU hal
$this->assertEquals(2, multiplicate(1, 2));
$this->assertEquals(5, multiplicate(3, 2));
$this->assertEquals(4, multiplicate(2, 2));
}
}
?>
Contoh Unit Testing Multiplikasi
$>phpunit BelajarUnitTest MultiplikasiTest.php
PHPUnit 3.5.12 by Sebastian Bergmann.
F
Time: 0 seconds, Memory: 3.00Mb
There was 1 failure:
1) BelajarUnitTest::testMultiplicate
Failed asserting that <integer:6> matches expected <integer:5>.
$\MultiplikasiTest.php:10
FAILURES!
Tests: 1, Assertions: 2, Failures: 1.
Contoh Unit Testing Multiplikasi
<?php
//MultiplikasiTest.php
require_once 'multiplikasi.php';
class BelajarUnitTest extends PHPUnit_Framework_TestCase{
public function testMultiplicate1And2(){
$this->assertEquals(2, multiplicate(1, 2));
}
public function testMultiplicate3And2(){
$this->assertEquals(5, multiplicate(3, 2));
}
public function testMultiplicate2And2(){
$this->assertEquals(4, multiplicate(2, 2));
}
}
?>
Contoh Unit Testing Multiplikasi
$>phpunit MultiplikasiTest.php
PHPUnit 3.5.12 by Sebastian Bergmann.
.F.
Time: 0 seconds, Memory: 3.00Mb
There was 1 failure:
1) BelajarUnitTest::testMultiplicate3And2
Failed asserting that <integer:6> matches expected <integer:5>.
$\MultiplikasiTest.php:12
FAILURES!
Tests: 3, Assertions: 3, Failures: 1.
Contoh Unit Testing Multiplikasi
<?php
//MultiplikasiTest.php
require_once 'multiplikasi.php';
class BelajarUnitTest extends PHPUnit_Framework_TestCase{
public function testMultiplicate1And2(){
$this->assertEquals(2, multiplicate(1, 2));
}
public function testMultiplicate3And2(){
$this->assertNotEquals(5, multiplicate(3, 2));
}
public function testMultiplicate2And2(){
$this->assertEquals(4, multiplicate(2, 2));
}
}
?>
Contoh Unit Testing Multiplikasi
$>phpunit BelajarUnitTest MultiplikasiTest.php
PHPUnit 3.5.12 by Sebastian Bergmann.
...
Time: 0 seconds, Memory: 3.00Mb
OK (3 tests, 3 assertions)
Contoh Unit Testing Multiplikasi
$>phpunit --testdox BelajarUnitTest MultiplikasiTest.php
PHPUnit 3.5.12 by Sebastian Bergmann.
BelajarUnitTest
[x] Multiplicate 1 and 2
[x] Multiplicate 3 and 2
[x] Multiplicate 2 and 2
Tapi.. Code saya seperti ini..
<?php
// Connects to your Database
mysql_connect ( "your.hostaddress.com", "username", "password" ) or die ( mysql_error () );
mysql_select_db ( "Database_Name" ) or die ( mysql_error () );
//checks cookies to make sure they are logged in
if (isset ( $_SESSION ['ID_my_site'] )) {
$username = $_ SESSION ['ID_my_site'];
$pass = $_ SESSION ['Key_my_site'];
$check = mysql_query ( "SELECT * FROM users WHERE username = '$username'" ) or die ( mysql_error () );
$result = mysql_fetch_array ( $check );
foreach ( $result as $info ) {
//if the cookie has the wrong password, they are taken to the login page
if ($pass != $info ['password']) {
header ( "Location: login.php" );
} else { //otherwise they are shown the admin area
echo "<p>Admin Area</p>";
echo "<p>Your Content<p>";
echo "<a href=logout.php>Logout</a>";
}
}
} else {
//if the cookie does not exist, they are taken to the login screen
header ( "Location: login.php" );
}
?>
Core techniques
Refactoring
Separation of Concern
Inversion of Control (Dependency Injection)
Design by Contract
Design Patterns
Contoh Refactoring
<?php
namespace myapp\security;
use myapp\daos\interfaces\User;
use myapp\util\interfaces\Session;
class Authorize implements interfaces\Authorize{
private $userDAO;
private $session;
public function __construct(User $userDAO, Session $session) {
$this->userDAO = $userDAO;
$this->session = $session;
}
public function isAuthorize(){
if($this->session->isAuthenticate()){
$user = $this->userDAO->getByUsername($this->session->getUsername());
return $this->session->getPassword() === $user->getPassword();
}else{
return false;
}
}
}
?>
Mocks & Stubs
<?php
namespace myapp\mocks;
use myapp\daos\interfaces\User;
class UserPalsu implements User{
public function getById($id){
return null;
}
public function getByUsername($username){
return null;
}
public function getAll(){
return null;
}
}
?>
<?php
namespace myapp\mocks;
use myapp\util\interfaces\Session;
class SessionPalsu implements Session{
public function getUsername(){
return null;
}
public function getPassword(){
return null;
}
public function isAuthenticate(){
return false;
}
}
?>
Unit Test Authorize
<?php
use myapp\security\Authorize;
use myapp\mocks\SessionPalsu;
use myapp\mocks\UserPalsu;
class AuthorizeTest extends PHPUnit_Framework_TestCase {
public function testIsAuthorizeSessionNotAuthenticate(){
$userDAO = new UserPalsu();
$session = new SessionPalsu();
$authorize = new Authorize($userDAO, $session);
$this->assertFalse($authorize->isAuthorize());
}
}
?>
Mocks & Stubs
<?php
namespace myapp\mocks;
use myapp\models;
use myapp\daos\interfaces\User;
class UserPalsu implements User{
private $user;
public function getById($id){
return null;
}
public function getByUsername($username){
return $this->user;
}
public function getAll(){
return null;
}
public function
setUser(models\interfaces\User $user){
$this->user = $user;
}
}
?>
<?php
namespace myapp\mocks;
use myapp\util\interfaces\Session;
class SessionPalsu implements Session{
private $username;
private $password;
private $authenticate;
public function getUsername() {
return $this->username;
}
public function setUsername($username) {
$this->username = $username;
}
public function getPassword() {
return $this->password;
}
public function setPassword($password) {
$this->password = $password;
}
public function isAuthenticate(){
return $this->authenticate;
}
public function setAuthenticate($authenticate){
$this->authenticate = $authenticate;
}
}
?>
Unit Test Authorize
<?php
use myapp\models\User;
use myapp\security\Authorize;
use myapp\mocks\SessionPalsu;
use myapp\mocks\UserPalsu;
class AuthorizeTest extends PHPUnit_Framework_TestCase {
//public function testIsAuthorizeSessionNotAuthenticate(){...
public function testIsAuthorizeSessionAuthenticateButWrongPassword(){
$userDAO = new UserPalsu();
$user = new User("username", "password");
$userDAO->setUser($user);
$session = new SessionPalsu();
$session->setPassword("wrongpassword");
$authorize = new Authorize($userDAO, $session);
$this->assertFalse($authorize->isAuthorize());
}
public function testIsAuthorizeSessionAuthenticateCorrectPassword(){
$userDAO = new UserPalsu();
$user = new User("username", "password");
$userDAO->setUser($user);
$session = new SessionPalsu();
$session->setPassword("password");
$authorize = new Authorize($userDAO, $session);
$this->assertTrue($authorize->isAuthorize());
}
}
?>
Mengorganisir Unit Test
Fixture
Bootstrap
Stubs & Mocks Tools
Test Suite
Code Coverage
Fixtures
<?php
use myapp\models\User;
use myapp\security\Authorize;
use myapp\mocks\SessionPalsu;
use myapp\mocks\UserPalsu;
class AuthorizeTest extends PHPUnit_Framework_TestCase {
private $userDAO;
private $session;
public function setUp(){
$this->userDAO = new UserPalsu();
$user = new User("username", "password");
$this->userDAO->setUser($user);
$this->session = new SessionPalsu();
$this->session->setAuthenticate(true);
}
public function tearDown(){
$this->userDAO = null;
$this->session = null;
}
//public function testIsAuthorizeSessionNotAuthenticate(){..
public function testIsAuthorizeSessionAuthenticateButWrongPassword(){
$this->session->setPassword("wrongpassword");
$authorize = new Authorize($this->userDAO, $this->session);
$this->assertFalse($authorize->isAuthorize());
}
public function testIsAuthorizeSessionAuthenticateCorrectPassword(){
$this->session->setPassword("password");
$authorize = new Authorize($this->userDAO, $this->session);
$this->assertTrue($authorize->isAuthorize());
}
}?>
Bootstrap
<phpunit bootstrap="/path/to/bootstrap.php"
colors="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnFailure="true">
<!-- ... -->
</phpunit>
Stubs and Mocks Tools
<?php
use myapp\security\Authorize;
class AuthorizeTest extends PHPUnit_Framework_TestCase {
private $userDAO;
private $session;
public function setUp() {
$this->userDAO = $this->getMock ( '\myapp\daos\User' );
$user = $this->getMock ( 'myapp\models\User', array(), array(), '', false, false, false );
$user->expects ( $this->any () )->method ( 'getUsername' )->will ( $this->returnValue ( 'username' ) );
$user->expects ( $this->any () )->method ( 'getPassword' )->will ( $this->returnValue ( 'password' ) );
$this->userDAO->expects ( $this->any () )->method ( 'getByUsername' )->will ( $this->returnValue ( $user ) );
$this->session = $this->getMock ( '\myapp\util\Session', array(), array(), '', false, false, false );
$this->session->expects ( $this->any () )->method ( 'isAuthenticate' )->will ( $this->returnValue ( true ) );
}
public function tearDown() {
$this->userDAO = null;
$this->session = null;
}
//public function testIsAuthorizeSessionNotAuthenticate(){...
public function testIsAuthorizeSessionAuthenticateButWrongPassword() {
$this->session->expects ( $this->any () )->method ( 'getPassword' )->will ( $this->returnValue ( 'wrongpassword' ) );
$authorize = new Authorize ( $this->userDAO, $this->session );
$this->assertFalse ( $authorize->isAuthorize () );
}
public function testIsAuthorizeSessionAuthenticateCorrectPassword() {
$this->session->expects ( $this->any () )->method ( 'getPassword' )->will ( $this->returnValue ( 'password' ) );
$authorize = new Authorize ( $this->userDAO, $this->session );
$this->assertTrue ( $authorize->isAuthorize () );
}
}
?>
Test Suite File SystemObject Tests
|-- Freezer |-- Freezer
| |-- HashGenerator | |-- HashGenerator
| | `-- NonRecursiveSHA1.php | | `-- NonRecursiveSHA1Test.php
| |-- HashGenerator.php | |
| |-- IdGenerator | |-- IdGenerator
| | `-- UUID.php | | `-- UUIDTest.php
| |-- IdGenerator.php | |
| |-- LazyProxy.php | |
| |-- Storage | |-- Storage
| | `-- CouchDB.php | | `-- CouchDB
| | | | |-- WithLazyLoadTest.php
| | | | `-- WithoutLazyLoadTest.php
| |-- Storage.php | |-- StorageTest.php
| `-- Util.php | `-- UtilTest.php
`-- Freezer.php `-- FreezerTest.php
$>phpunit Tests
PHPUnit 3.5.13 by Sebastian Bergmann.
............................................................ 60 / 75
...............
Time: 0 seconds
OK (75 tests, 164 assertions)
Test Suite XML
<phpunit>
<testsuites>
<testsuite name="Object_Freezer">
<file>Tests/Freezer/HashGenerator/NonRecursiveSHA1Test.php</file>
<file>Tests/Freezer/IdGenerator/UUIDTest.php</file>
<file>Tests/Freezer/UtilTest.php</file>
<file>Tests/FreezerTest.php</file>
<file>Tests/Freezer/StorageTest.php</file>
<file>Tests/Freezer/Storage/CouchDB/WithLazyLoadTest.php</file>
<file>Tests/Freezer/Storage/CouchDB/WithoutLazyLoadTest.php</file>
</testsuite>
</testsuites>
</phpunit>
Code Coverage
Memulai Unit Testing
Yakinkan diri sendiri
Kumpulkan Case Study atau Success Story http://biblio.gdinwiddie.com/biblio/StudiesOfTestDrivenDevelopment
Cari Pendukung / Panggil Konsultan
Tentukan Tujuan
Catat Perkembangan
Bersiap menghadapi rintangan
Kemana Setelah Ini
Metodologi
Test Driven Development
Behaviour Driven Development
Tools Pendukung
IDE (Zend Studio, PHPStorm, PDT, NetBeans)
Build Automation (Ant, Maven, Phing)
Continuous Integration
Test Lain menggunakan PHPUnit
Database Testing
UI Testing (Selenium)
Diskusi terbuka di Milist / Groups
Referensi
Wikipedia http://en.wikipedia.org/wiki/Unit_testing
„PHPUnit Manual“ – Sebastian Bergmann http://www.phpunit.de/manual/current/en/
„Getting Started With PHPUnit “ – Sebastian Bergmann http://www.slideshare.net/sebastian_bergmann/getting-started-with-phpunit
„The Art of Unit Testing“ - Roy Osherove
„Unit Testing Fundamentals“ – Richard Paul http://www.slideshare.net/rapaul/unit-testing-fundamentals
„Embrace Unit Testing“ – Alessio Pace http://www.slideshare.net/alessiopace/embrace-unit-testing
Web: http://arthur.purnama.de
Mail: arthur [at] purnama.de
Twitter: @purnama
Groups: