Date post: | 02-Aug-2015 |
Category: |
Engineering |
Upload: | michelangelo-van-dam |
View: | 293 times |
Download: | 5 times |
Create, Test, Secure & Repeat
Part of the in2it Quality Assurance Training
in it2PROFESSIONAL PHP SERVICES
http
s://w
ww.
flick
r.com
/pho
tos/
9037
1939
@N
00/4
3448
7810
4
Michelangelo van DamPHP Consultant, Community Leader & Trainer
http
s://w
ww.
flick
r.com
/pho
tos/
akra
bat/8
7843
1881
3
http
s://w
ww.
flick
r.com
/pho
tos/
erik
torn
er/8
0484
2872
9
Introduction
Mise en place (preparation)
Running Tests
Starting a new project with TDD
Testing & Improving legacy code
Other tools
Recap & Closing remarks
http
s://w
ww.
flick
r.com
/pho
tos/
ryan
tyle
rsm
ith/1
4010
1048
72
Introduction
Mise en place (preparation)
Running Tests
Starting a new project with TDD
Testing & Improving legacy code
Other tools
Recap & Closing remarks
http
s://w
ww.
flick
r.com
/pho
tos/
ryan
tyle
rsm
ith/1
4010
1048
72
PHPUnit• Created by Sebastian
Bergmann in 2004
• Port of xUnit to PHP
• Uses assertions for testing
• Supports
• Unit testing
• Integration testing w/ DBUnit
• Acceptance testing w/ Selenium
http
s://w
ww.
flick
r.com
/pho
tos/
gree
nboy
/389
5219
425
http
s://w
ww.
flick
r.com
/pho
tos/
gree
nboy
/389
5219
425
http
s://w
ww.
flick
r.com
/pho
tos/
toni
vc/
http
s://w
ww.
flick
r.com
/pho
tos/
gree
nboy
/389
5219
425
http
s://w
ww.
flick
r.com
/pho
tos/
toni
vc/
http
s://w
ww.
flick
r.com
/pho
tos/
6875
1915
@N
05/6
7361
5804
5
http
s://w
ww.
flick
r.com
/pho
tos/
akra
bat/8
4215
6017
8
http
s://w
ww.
flick
r.com
/pho
tos/
jake
rust
/162
2366
9794
Who created PHPUnit and is now project lead?
A. Chris Hartjes
B. Sebastian Bergmann
C. Stefan Priepsch
With PHPUnit you can…?
A. Test the smallest functional piece of code (unit)
B. Test integrations with a database (integration)
C. Test automated acceptance testing with Selenium
D. All of the above
E. None of the above
An assertion is…?
A. Verifying that an expected value matches the result of a process?
B. Verifying that a process produces results
C. A transformation of a value
Assertion
• is a true/false statement
• to match an expectation
• with the result of functionality under test
Available assertions• assertArrayHasKey() • assertArraySubset() • assertClassHasAttribute() • assertClassHasStaticAttribute() • assertContains() • assertContainsOnly() • assertContainsOnlyInstancesOf() • assertCount() • assertEmpty() • assertEqualXMLStructure() • assertEquals() • assertFalse() • assertFileEquals() • assertFileExists() • assertGreaterThan() • assertGreaterThanOrEqual() • assertInstanceOf() • assertInternalType() • assertJsonFileEqualsJsonFile()
• assertJsonStringEqualsJsonFile() • assertJsonStringEqualsJsonString() • assertLessThan() • assertLessThanOrEqual() • assertNull() • assertObjectHasAttribute() • assertRegExp() • assertStringMatchesFormat() • assertStringMatchesFormatFile() • assertSame() • assertStringEndsWith() • assertStringEqualsFile() • assertStringStartsWith() • assertThat() • assertTrue() • assertXmlFileEqualsXmlFile() • assertXmlStringEqualsXmlFile() • assertXmlStringEqualsXmlString()
Available assertions• assertArrayHasKey() • assertArraySubset() • assertClassHasAttribute() • assertClassHasStaticAttribute() • assertContains() • assertContainsOnly() • assertContainsOnlyInstancesOf() • assertCount()!• assertEmpty()!• assertEqualXMLStructure() • assertEquals()!• assertFalse()!• assertFileEquals() • assertFileExists() • assertGreaterThan() • assertGreaterThanOrEqual() • assertInstanceOf() • assertInternalType() • assertJsonFileEqualsJsonFile()
• assertJsonStringEqualsJsonFile() • assertJsonStringEqualsJsonString() • assertLessThan() • assertLessThanOrEqual() • assertNull()!• assertObjectHasAttribute() • assertRegExp() • assertStringMatchesFormat() • assertStringMatchesFormatFile() • assertSame()!• assertStringEndsWith() • assertStringEqualsFile() • assertStringStartsWith() • assertThat() • assertTrue()!• assertXmlFileEqualsXmlFile() • assertXmlStringEqualsXmlFile() • assertXmlStringEqualsXmlString()
Example usage Assertions
// Asserting a value returned by $myClass->myMethod() is TRUE "$this->assertTrue($myClass->myMethod()); "!// Asserting that a string matches type and value of $myClass->toString() "$this->assertSame('my string', $myClass->toString()); "!// Asserting that the value matches $myClass->addOne(0), type check not necessary "$this->assertEquals('1', $myClass->addOne(0)); "!// Assserting that the result of $myClass->getBirthday()->format('Y') "// is greater than the expected value "$this->assertGreaterThan(1900, $myClass->getBirthday()->format('Y')); "!// Asserting a value is NULL with a specified error message "$this->assertNull( " $myClass->getProperty(), " 'When instantiating MyClass the property value should be NULL but is ' . $myClass->getProperty() ");
Annotations
• Provide automatic features
• to execute arbitrary functionality
• without having to create logic
Available annotations• @author • @after • @afterClass • @backupGlobals • @backupStaticAttributes • @before • @beforeClass • @codeCoverageIgnore* • @covers • @coversDefaultClass • @coversNothing • @dataProvider • @depends • @expectedException • @expectedExceptionCode
• @expectedExceptionMessage • @expectedExceptionMessageRegExp • @group • @large • @medium • @preserveGlobalState • @requires • @runTestsInSeparateProcesses • @runInSeparateProcess • @small • @test • @testdox • @ticket • @uses
@group<?php "!class OrderTest extends \PHPUnit_Framework_TestCase "{ " /** " * @group Order " */ " public function testCanCreateOrder() " { " // ... test logic goes here " } " " /** " * @group Order " * @group BUG-1234 " */ " public function testOrdersCanNotContainSoldProducts() " { " // ... test logic goes here " } "}
How to use @group
# Run phpunit only against tests for the Order module./vendor/bin/phpunit --group Order!# Run phpunit for all tests except for the Order module./vendor/bin/phpunit --exclude-group Order
@dataProvider<?php "!class OrderTest extends \PHPUnit_Framework_TestCase "{ " public function badDataProvider() " { " return [ " [0, 0, 0], // we don't accept ID's less or equal than 0 " ['', new \stdClass(), []], // only integer and float values " [null, null, null], // no NULL values allowed " ]; " } " " /** " * @dataProvider badDataProvider " */ " public function testAddProductToOrder($orderId, $productId, $price) " { " $order = new Order(); " $result = $order->addProductToOrder($orderId, $productId, $price); " $this->assertFalse($result); " } "}
@expectedException<?php "!class OrderTest extends \PHPUnit_Framework_TestCase "{ " /** " * @expectedException \InvalidArgumentException " * @expectedExceptionMessage The order ID cannot be null " */ " public function testAddProductToOrder() " { " $orderId = null; " $productId = null; " $price = null; " $order = new Order(); " $result = $order->addProductToOrder($orderId, $productId, $price); " $this->fail('Expected exception was not thrown'); " } "}
Introduction
Mise en place (preparation)
Running Tests
Starting a new project with TDD
Testing & Improving legacy code
Other tools
Recap & Closing remarks
http
s://w
ww.
flick
r.com
/pho
tos/
ryan
tyle
rsm
ith/1
4010
1048
72
Getting PHPUnit
Composer way (preferred) Download Composer curl -sS https://getcomposer.org/installer | php!Get phpunit php composer.phar require phpunit/phpunit!Direct download curl -O https://phar.phpunit.de/phpunit.phar
http
s://w
ww.
flick
r.com
/pho
tos/
ruef
ul/5
9065
4659
9
For this trainingInstallation of source code !Clone the repository into your workspace before attending the workshop. git clone https://github.com/in2it/ctsr-workshop.git cd ctsr-workshop/!Once you have cloned the training package, make sure you install composer. curl -sS https://getcomposer.org/installer | php!When the download is done, install required components using composer php composer.phar install
For this trainingInstallation of source code !Clone the repository into your workspace before attending the workshop. git clone https://github.com/in2it/ctsr-workshop.git cd ctsr-workshop/!Once you have cloned the training package, make sure you install composer. curl -sS https://getcomposer.org/installer | php!When the download is done, install required components using composer php composer.phar install ht
tps:
//ww
w.fli
ckr.c
om/p
hoto
s/in
telfr
eepr
ess/
1398
3474
320
During the workshop you're asked to solve several exercises. All example codes are based on a UNIX-like OS, so if you plan to participate this workshop with another OS, you need to know what changes are required to have the exercises run on your operating system.The exercises, the source code and the examples are tested on the following platforms: • Windows 7 • Mac OS X • Ubuntu Linux !When you need to switch to a specific exercise branch (e.g. ex-0.0), you can do this with the following command. git checkout -b ex-0.0 origin/ex-0.0
http
s://w
ww.
flick
r.com
/pho
tos/
rhin
onea
l/806
0238
470
<?xml version="1.0" encoding="utf-‐8"?> <phpunit bootstrap="./vendor/autoload.php" colors="true" stopOnFailure="true" stopOnError="true" syntaxCheck="true"> ! <testsuite name="Unit Tests"> <directory>./tests</directory> </testsuite> ! <filter> <whitelist> <directory suffix=".php">./src</directory> </whitelist> </filter> ! <logging> <log type="coverage-‐html" target="./build/coverage" charset="UTF-‐8" yui="true" highlight="true" lowUpperBound="35" highLowerBound="70"/> <log type="coverage-‐xml" target="./build/logs/coverage.xml"/> <log type="coverage-‐clover" target="./build/logs/clover.xml"/> <log type="tap" target="./build/logs/phpunit.tap"/> <log type="testdox-‐text" target="./build/logs/testdox.txt"/> <log type="junit" target="./build/logs/junit.xml" logIncompleteSkipped="false"/> </logging> !</phpunit>
Introduction
Mise en place (preparation)
Running Tests
Starting a new project with TDD
Testing & Improving legacy code
Other tools
Recap & Closing remarks
http
s://w
ww.
flick
r.com
/pho
tos/
ryan
tyle
rsm
ith/1
4010
1048
72
Exercise 0.0
• What will happen when you run PHPUnit now?
• Checkout branch ex-0.0 git checkout -b ex-0.0 origin/ex-0.0
• Run PHPUnit
Let’s write our first test<?php "namespace In2it\Test\Workshop\Ctsr; "!use In2it\Workshop\Ctsr\SampleClass; "!class SampleClassTest extends \PHPUnit_Framework_TestCase "{ " public function testSomethingReturnsGreeting() " { " $sampleClass = new SampleClass(); " $this->assertSame( " 'Hello World!', $sampleClass->doSomething() " ); " } "}
Exercise 0.1
• What will happen when you run PHPUnit now?
• Checkout branch ex-0.1 git checkout -b ex-0.1 origin/ex-0.1
• Run PHPUnit
Write our class
<?php "namespace In2it\Workshop\Ctsr; "!class SampleClass "{ " public function doSomething() " { " return 'Hello World!'; " } "}
Exercise 0.2
• What will happen when you run PHPUnit now?
• Checkout branch ex-0.2 git checkout -b ex-0.2 origin/ex-0.2
• Run PHPUnit
Exercise 0.3
• Test that you can provide an argument and the argument will be returned as “Hello <arg>!”
• Write the test
• Modify the class
Modifying the test class
public function testSomethingReturnsArgument() " { " $sampleClass = new SampleClass(); " $argument = 'Class'; " $this->assertSame( " sprintf('Hello %s!', $argument), " $sampleClass->doSomething($argument) " ); " }
Modify the SampleClass
<?php "namespace In2it\Workshop\Ctsr; "!class SampleClass "{ " public function doSomething($argument) " { " return 'Hello ' . $argument . '!'; " } "}
Update further
<?php "namespace In2it\Workshop\Ctsr; "!class SampleClass "{ " public function doSomething($argument = 'World') " { " return 'Hello ' . $argument . '!'; " } "}
Chapter 0 What have you learned
• How to install phpunit
• How to configure phpunit
• How to write your test first
• How to modify requirements through testing
• How to debug failures and fix them easily with tests
http
s://w
ww.
flick
r.com
/pho
tos/
rast
er/3
5631
3580
4
Introduction
Mise en place (preparation)
Running Tests
Starting a new project with TDD
Testing & Improving legacy code
Other tools
Recap & Closing remarks
http
s://w
ww.
flick
r.com
/pho
tos/
ryan
tyle
rsm
ith/1
4010
1048
72
http
s://w
ww.
flick
r.com
/pho
tos/
esot
erik
a/53
4799
8757
Functional requirements
• Application needs to manage cron entries, where all scheduled tasks are stored in a database and written to the crontab when saved.
What is crontab?• A tool on UNIX-like systems to execute tasks at a certain interval
• Each line contains the following items:
• Minutes and/or interval
• Hours and/or interval
• Days of the month (DOM) and/or interval
• Months and/or interval
• Days of the week (DOW) and/or interval
• Executable command
Example crontab # Minutes Hours DOM Months DOW Command!# Warm up caches with new products# Every 5 minutes each day*/5 * * * * /bin/sh /path/to/productCollector.sh 2>&1!# Send marketing mail to active customers# Every Monday at 9:30am30 9 * * 1 /usr/bin/php /path/to/marketingMailSender.php 2>&1!# Clean up waste# Every day at 6am, 1pm and 5pm from Monday to Friday0 6,13,17 * * 1-5 /bin/sh /path/to/wasteCleaner.sh 2>&1
Analysis• crontab = collection of entries • Each entry contains 5 assets and a command • Each asset can contain a
• Wildcard (full range) • Range n-m (subset of full range) • List n,m,o • A single value n
• With similar intervals (without the wildcard)
http
s://w
ww.
flick
r.com
/pho
tos/
cave
man
_922
23/3
9683
5438
7
• cronmanager • collection of crontab entries • each entry contains
• minutes (collection) and interval (collection) • hours (collection) and interval (collection) • days of the month (collection) and interval (collection) • months (collection) and interval (collection) • days of the week (collection) and interval (collection) • command string
• each entry collection (minutes, hours, dom, months, dow) requires a range • minutes (0 - 59) • hours (0 - 23) • days of the month (1 - 31) • month (1 - 12) • days of the week (0 - 7) (0 & 7 are Sunday)
• crontab is write-only, so we need to update the full crontab completely
<?php "namespace In2it\Test\Workshop\Ctsr; "!class CronManagerTest extends \PHPUnit_Framework_TestCase "{ "! public function testCronManagerCanAddAnEntry() " { " $entry = new \stdClass(); " $cronman = new CronManager(); " $cronman->addEntry($entry); "! $this->assertCount(1, $cronman); " } "}
<?php "namespace In2it\Workshop\Ctsr; "!class CronManager implements \Countable, \Iterator "{ " protected $stack; " protected $pointer; " protected $counter; "! public function addEntry($entry) " { " $this->stack[] = $entry; " $this->counter++; " } "! // Implement the Iterator and Countable methods here. " // - Iterator: http://php.net/manual/en/class.iterator.php " // - Countable: http://php.net/manual/en/class.countable.php " public function current() {/**... */} " public function next() {/**... */} " public function key() {/**... */} " public function valid() {/**... */} " public function rewind() {/**... */} " public function count() {/**... */} "}
!<?php "namespace In2it\Test\Workshop\Ctsr; "!use In2it\Workshop\Ctsr\CronManager; "!class CronManagerTest extends \PHPUnit_Framework_TestCase "{ "! public function testCronManagerCanAddAnEntry() " { " $entry = new \stdClass(); " $cronman = new CronManager(); " $cronman->addEntry($entry); "! $this->assertCount(1, $cronman); " } "}
!<?php "namespace In2it\Test\Workshop\Ctsr; "!use In2it\Workshop\Ctsr\CronManager; "!class CronManagerTest extends \PHPUnit_Framework_TestCase "{ "! public function testCronManagerCanAddAnEntry() " { " $entry = new \stdClass(); " $cronman = new CronManager(); " $cronman->addEntry($entry); "! $this->assertCount(1, $cronman); " } "}
Something like this…
<?php "namespace In2it\Test\Workshop\Ctsr; "!use In2it\Workshop\Ctsr\CronManager\Entry; "!class EntryTest extends \PHPUnit_Framework_TestCase "{ " public function testEntryContainsAllFields() { /** ... */ } "! public function testEntryCanSetEntryElements() { /** ... */ } "}
Something like this… (2)
public function testEntryContainsAllFields() "{ " $entry = new Entry(); "! $this->assertCount(0, $entry->getMinutes()); " $this->assertCount(0, $entry->getHours()); " $this->assertCount(0, $entry->getDom()); " $this->assertCount(0, $entry->getMonths()); " $this->assertCount(0, $entry->getDow()); " $this->assertSame('', $entry->getCommand()); "}
Something like this… (3)public function testEntryCanSetEntryElements() "{ " $assetCollection = $this->getMock( " 'In2it\\Workshop\\Ctsr\\CronManager\\AssetCollection' " ); " $entry = new Entry(); "! $entry->setMinutes($assetCollection); " $this->assertInstanceOf( " 'In2it\\Workshop\\Ctsr\\CronManager\\AssetCollection', " $entry->getMinutes() " ); "! /* " * Similar routines for Hours, Days of the Month, Months and Days of the week " */ "! $command = $this->getMock('In2it\\Workshop\\Ctsr\\CronManager\\Command'); " $entry->setCommand($command); " $this->assertInstanceOf( " 'In2it\\Workshop\\Ctsr\\CronManager\\Command', " $entry->getCommand() " ); "}
Overview of classes (ex-1.0) ctsr-workshop/ src/ ex-1.0/ CronManager.php tests/ ex-1.0/ CronManager/ EntryTest.php CronManagerTest.php
Pop-quiz
// Why are we using Mock objects to test functionality? "$assetCollection = $this->getMock( " 'In2it\\Workshop\\Ctsr\\CronManager\\AssetCollection' "); "!$asset = $this->getMock( " 'In2it\\Workshop\\Ctsr\\CronManager\\Asset' "); "!$entry = $this->getMock( " 'In2it\\Workshop\\Ctsr\\CronManager\\Entry' ");
Create some bad data
public function badDataProvider() "{ " return array ( " array ('foo'), " array (new \stdClass()), " array (array ()), " array (1.50), " ); "}
And let’s test it!
/** " * @dataProvider badDataProvider " * @covers In2it\Workshop\Ctsr\CronManager\Asset::__construct " * @covers In2it\Workshop\Ctsr\CronManager\Asset::setValue " * @covers In2it\Workshop\Ctsr\CronManager\Asset::getValue " * @expectedException \InvalidArgumentException " */ "public function testRejectBadData($badData) "{ " $asset = new Asset($badData); " $this->fail('Expected InvalidArgumentException to be thrown'); "}
Let’s fix that!/** " * @param int $value " * @throws \InvalidArgumentException " */ "public function setValue($value) "{ " if (!is_int($value)) { " throw new \InvalidArgumentException( " 'You\'ve provided an invalid argument' " ); " } " if (false === ($result = filter_var($value, FILTER_VALIDATE_INT))) { " throw new \InvalidArgumentException( " 'You\'ve provided an invalid argument' " ); " } " $this->value = (int) $value; "}
Introduction
Mise en place (preparation)
Running Tests
Starting a new project with TDD
Testing & Improving legacy code
Other tools
Recap & Closing remarks
http
s://w
ww.
flick
r.com
/pho
tos/
ryan
tyle
rsm
ith/1
4010
1048
72
http
s://w
ww.
flick
r.com
/pho
tos/
cmdr
cord
/964
5186
380
Legacy code
• Code that was already written
• Not (always) adhering to best practices
• Not (always) testable
• What developers hate working on
http
s://w
ww.
flick
r.com
/pho
tos/
arch
er10
/784
5300
746
<?php "!class ModuleManager "{ " public static $modules_install = array(); " /** " * Includes file with module installation class. " * " * Do not use directly. " * " * @param string $module_class_name module class name - underscore separated " * @return bool " */ " public static final function include_install($module_class_name) { " if(isset(self::$modules_install[$module_class_name])) return true; " $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . 'Install.php'; " if (!file_exists($full_path)) return false; " ob_start(); " $ret = require_once($full_path); " ob_end_clean(); " $x = $module_class_name.'Install'; " if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) " trigger_error('Module ' . $module_class_name . ': Invalid install file', E_USER_ERROR); " self::$modules_install[$module_class_name] = new $x($module_class_name); " return true; " } "!}
<?php "!include_once __DIR__ . '/../../src/ex-2.0/ModuleManager.php'; "!class ModuleManagerTest extends \PHPUnit_Framework_TestCase "{ " /** " * @covers ModuleManager::include_install " */ " public function testModuleManagerCanLoadMailModule() " { " $result = ModuleManager::include_install('Mail'); " $this->assertTrue($result); " } "}
<?php "!class ModuleManager "{ " public static $modules_install = array(); " /** " * Includes file with module installation class. " * " * Do not use directly. " * " * @param string $module_class_name module class name - underscore separated " * @return bool " */ " public static final function include_install($module_class_name) { " if(isset(self::$modules_install[$module_class_name])) return true; " $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . 'Install.php'; " if (!file_exists($full_path)) return false; " ob_start(); " $ret = require_once($full_path); " ob_end_clean(); " $x = $module_class_name.'Install'; " if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) " trigger_error('Module ' . $module_class_name . ': Invalid install file', E_USER_ERROR); " self::$modules_install[$module_class_name] = new $x($module_class_name); " return true; " } "!}
<?php "!class ModuleManager "{ " public static $modules_install = array(); " /** " * Includes file with module installation class. " * " * Do not use directly. " * " * @param string $module_class_name module class name - underscore separated " * @return bool " */ " public static final function include_install($module_class_name) { " if(isset(self::$modules_install[$module_class_name])) return true; " $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . 'Install.php'; " if (!file_exists($full_path)) return false; " ob_start(); " $ret = require_once($full_path); " ob_end_clean(); " $x = $module_class_name.'Install'; " if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) " trigger_error('Module ' . $module_class_name . ': Invalid install file', E_USER_ERROR); " self::$modules_install[$module_class_name] = new $x($module_class_name); " return true; " } "!}
<?php "!class ModuleManager "{ " public static $modules_install = array(); " /** " * Includes file with module installation class. " * " * Do not use directly. " * " * @param string $module_class_name module class name - underscore separated " * @return bool " */ " public static final function include_install($module_class_name) { " if(isset(self::$modules_install[$module_class_name])) return true; " $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . 'Install.php'; " if (!file_exists($full_path)) return false; " ob_start(); " $ret = require_once($full_path); " ob_end_clean(); " $x = $module_class_name.'Install'; " if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) " trigger_error('Module ' . $module_class_name . ': Invalid install file', E_USER_ERROR); " self::$modules_install[$module_class_name] = new $x($module_class_name); " return true; " } "!}
<?php "!class ModuleManager "{ " public static $modules_install = array(); " /** " * Includes file with module installation class. " * " * Do not use directly. " * " * @param string $module_class_name module class name - underscore separated " * @return bool " */ " public static final function include_install($module_class_name) { " if(isset(self::$modules_install[$module_class_name])) return true; " $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . 'Install.php'; " if (!file_exists($full_path)) return false; " ob_start(); " $ret = require_once($full_path); " ob_end_clean(); " $x = $module_class_name.'Install'; " if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) " trigger_error('Module ' . $module_class_name . ': Invalid install file', E_USER_ERROR); " self::$modules_install[$module_class_name] = new $x($module_class_name); " return true; " } "!}
<?php "!class ModuleManager "{ " public static $modules_install = array(); " /** " * Includes file with module installation class. " * " * Do not use directly. " * " * @param string $module_class_name module class name - underscore separated " * @return bool " */ " public static final function include_install($module_class_name) { " if(isset(self::$modules_install[$module_class_name])) return true; " $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . 'Install.php'; " if (!file_exists($full_path)) return false; " ob_start(); " $ret = require_once($full_path); " ob_end_clean(); " $x = $module_class_name.'Install'; " if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) " trigger_error('Module ' . $module_class_name . ': Invalid install file', E_USER_ERROR); " self::$modules_install[$module_class_name] = new $x($module_class_name); " return true; " } "!}
<?php "!include_once __DIR__ . '/../../src/ex-2.0/ModuleManager.php'; "!class ModuleManagerTest extends \PHPUnit_Framework_TestCase "{ " /** " * @covers ModuleManager::include_install " */ "// public function testModuleManagerCanLoadMailModule() "// { "// $result = ModuleManager::include_install('Mail'); "// $this->assertTrue($result); "// } "}
http
s://w
ww.
flick
r.com
/pho
tos/
mar
cgbx
/780
3086
292
/** " * @covers ModuleManager::include_install " */ "public function testReturnImmediatelyWhenModuleAlreadyLoaded() "{ " $module = 'Foo_Bar'; " ModuleManager::$modules_install[$module] = 1; " $result = ModuleManager::include_install($module); " $this->assertTrue($result); " $this->assertCount(1, ModuleManager::$modules_install); "}
http
s://w
ww.
flick
r.com
/pho
tos/
chris
tian_
joha
nnes
en/2
2482
4478
6
/** " * @covers ModuleManager::include_install " */ "public function testReturnWhenModuleIsNotFound() "{ " $module = 'Foo_Bar'; " $result = ModuleManager::include_install($module); " $this->assertFalse($result); " $this->assertEmpty(ModuleManager::$modules_install); "}
public static final function include_install($module_class_name) { " if(isset(self::$modules_install[$module_class_name])) return true; " $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . 'Install.php'; " if (!file_exists($full_path)) return false; " ob_start(); " $ret = require_once($full_path); " ob_end_clean(); " $x = $module_class_name.'Install'; " if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) " trigger_error('Module ' . $module_class_name . ': Invalid install file', E_USER_ERROR); " self::$modules_install[$module_class_name] = new $x($module_class_name); " return true; "}
self::$modules_install[$module_class_name]
http
s://w
ww.
flick
r.com
/pho
tos/
evae
kebl
ad/1
4780
0905
50
/** " * @covers ModuleManager::include_install " * @expectedException \PHPUnit_Framework_Error " */ "public function testTriggerErrorWhenInstallClassDoesNotExists() "{ " $module = 'EssClient'; " $result = ModuleManager::include_install($module); " $this->fail('Expecting loading module EssClient would trigger and error'); "}
public static final function include_install($module_class_name) { " if(isset(self::$modules_install[$module_class_name])) return true; " $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . 'Install.php'; " if (!file_exists($full_path)) return false; " ob_start(); " $ret = require_once($full_path); " ob_end_clean(); " $x = $module_class_name.'Install'; " if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) " trigger_error('Module ' . $module_class_name . ': Invalid install file', E_USER_ERROR); " self::$modules_install[$module_class_name] = new $x($module_class_name); " return true; "}
public static final function include_install($module_class_name) { " if(isset(self::$modules_install[$module_class_name])) return true; " $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . 'Install.php'; " if (!file_exists($full_path)) return false; " ob_start(); " $ret = require_once($full_path); " ob_end_clean(); " $x = $module_class_name.'Install'; " if(!(class_exists($x, false)) || !array_key_exists('ModuleInstall',class_parents($x))) " trigger_error('Module ' . $module_class_name . ': Invalid install file', E_USER_ERROR); " self::$modules_install[$module_class_name] = new $x($module_class_name); " return true; "}
if (!file_exists($full_path)) return false;
Current Filestructure
|-- ModuleManager.php "`-- modules " |-- EssClient " | `-- EssClient.php " |-- IClient " | `-- IClientInstall.php " `-- Mail " `-- MailInstall.php
http
s://w
ww.
flick
r.com
/pho
tos/
fragi
lete
nder
/533
2586
299
Current Filestructure
|-- ModuleManager.php "`-- modules " |-- EssClient " | `-- EssClient.php " |-- IClient " | `-- IClientInstall.php " `-- Mail " `-- MailInstall.php
/** " * @covers ModuleManager::include_install " * @expectedException \PHPUnit_Framework_Error " */ "public function testTriggerErrorWhenInstallClassDoesNotExists() "{ " $module = 'IClient'; " $result = ModuleManager::include_install($module); " $this->fail('Expecting loading module EssClient would trigger and error'); "}
/** " * @covers ModuleManager::include_install " */ "public function testModuleManagerCanLoadMailModule() "{ " $result = ModuleManager::include_install('Mail'); " $this->assertTrue($result); "}
Get the codebranch ex-2.0
http
s://w
ww.
flick
r.com
/pho
tos/
ahhy
eah/
4544
9439
6
/** " * Process Bank Payment files " */ " public function processBankPayments() " { " $this->getLogger()->log('Starting bank payment process', Zend_Log::INFO); " foreach ($this->_getBankFiles() as $bankFile) { " $bankData = $this->_processBankFile($bankFile); " $this->getLogger()->log('Processing ' . $bankData->transactionId, " Zend_Log::DEBUG " ); " /** @var Contact_Model_Contact $contact */ " $contact = $this->getMapper('Contact_Model_Mapper_Contact') " ->findContactByBankAccount($bankData->transactionAccount); " if (null !== $contact) { " $this->getLogger()->log(sprintf( " 'Found contact "%s" for bank account %s', " $contact->getName(), " $bankData->transactionAccount " ), Zend_Log::DEBUG); " $data = array ( " 'amount' => $bankData->transactionAmount, " 'payment_date' => $bankData->transactionDate " ); " $this->getMapper('Invoice_Model_Mapper_Payments') " ->updatePayment($data, " array ('contact_id = ?' => $contact->getContactId()) " ); " $this->_moveBankFile($bankFile, " $this->getPath() . DIRECTORY_SEPARATOR . self::PROCESS_SUCCEEDED " ); " } else { " $this->getLogger()->log(sprintf( " 'Could not match bankaccount "%s" with a contact', " $bankData->transactionAccount " ), Zend_Log::WARN); " $this->_moveBankFile($bankFile, " $this->getPath() . DIRECTORY_SEPARATOR . self::PROCESS_FAILED " ); " } " } " }
public function testProcessingBankPayments() " { " $contact = $this->getMock( " 'Contact_Model_Contact', " array ('getContactId', 'getName') " ); " $contact->expects($this->any()) " ->method('getContactId') " ->will($this->returnValue(1)); " $contact->expects($this->any()) " ->method('getName') " ->will($this->returnValue('Foo Bar')); " $contactMapper = $this->getMock('Contact_Model_Mapper_Contact', " array ('findContactByBankAccount') " ); " $contactMapper->expects($this->any()) " ->method('findContactByBankAccount') " ->will($this->returnValue($contact)); " $paymentsMapper = $this->getMock('Invoice_Model_Mapper_Payments', " array ('updatePayment') " ); "! $logMock = new Zend_Log_Writer_Mock(); " $logger = new Zend_Log(); " $logger->setWriter($logMock); " $logger->setPriority(Zend_Log::DEBUG); "! $as400 = new Payments_Service_As400(); " $as400->addMapper($contactMapper, 'Contact_Model_Mapper_Contact') " ->addMapper($paymentsMapper, 'Invoice_Model_Mapper_Payments') " ->setPath(__DIR__ . DIRECTORY_SEPARATOR . '_files') " ->setLogger($logger); "! $as400->processBankPayments(); " $this->assertCount(3, $logMock->events); " $this->assertEquals('Processing 401341345', $logMock->events[1]); " $this->assertEquals( " 'Found contact "Foo Bar" for bank account BE93522511513933', " $logMock->events[2] " ); " }
$as400 = new Payments_Service_As400(); " $as400->addMapper($contactMapper, 'Contact_Model_Mapper_Contact') " ->addMapper($paymentsMapper, 'Invoice_Model_Mapper_Payments') " ->setPath(__DIR__ . DIRECTORY_SEPARATOR . '_files') " ->setLogger($logger); "! $as400->processBankPayments(); " $this->assertCount(3, $logMock->events); " $this->assertEquals('Processing 401341345', $logMock->events[1]); " $this->assertEquals( " 'Found contact "Foo Bar" for bank account BE93522511513933', " $logMock->events[2] " );
Get the codebranch ex-2.1
http
s://w
ww.
flick
r.com
/pho
tos/
ahhy
eah/
4544
9439
6
Introduction
Mise en place (preparation)
Running Tests
Starting a new project with TDD
Testing & Improving legacy code
Other tools
Recap & Closing remarks
http
s://w
ww.
flick
r.com
/pho
tos/
ryan
tyle
rsm
ith/1
4010
1048
72
http
s://w
ww.
flick
r.com
/pho
tos/
floria
nric
/726
3382
550
Introduction
Mise en place (preparation)
Running Tests
Starting a new project with TDD
Testing & Improving legacy code
Other tools
Recap & Closing remarks
http
s://w
ww.
flick
r.com
/pho
tos/
ryan
tyle
rsm
ith/1
4010
1048
72
http
s://w
ww.
flick
r.com
/pho
tos/
didm
ysel
f/803
0013
349
http
s://w
ww.
flick
r.com
/pho
tos/
wjs
erso
n/33
1085
1114
http
s://w
ww.
flick
r.com
/pho
tos/
joes
hlab
otni
k/23
8449
5536
http
s://w
ww.
flick
r.com
/pho
tos/
muc
h0/8
5523
5390
1
http
s://w
ww.
flick
r.com
/pho
tos/
thom
asha
wk/
1049
0113
913
References
in it2PROFESSIONAL PHP SERVICES
Michelangelo van Dam!Zend Certified Engineer
[email protected] - www.in2it.be - T in2itvof - F in2itvof
PHPUnit!!
Getting Started!Advanced Testing
Zend Framework 2!!
Fundamentals!Advanced
Azure PHP!!
Quick time to market!Scale up and out
jQuery!!
Professional jQuery
PHP!!
PHP for beginners!Professional PHP
HTML & CSS!!
The Basics
Our training courses
http
s://w
ww.
flick
r.com
/pho
tos/
drew
m/3
1918
7251
5