testing untestable code - phpconpl11
TRANSCRIPT
Testing untestable code
Testing untestable code
About me
Stephan Hochdörfer, bitExpert AG
Department Manager Research Labs
enjoying PHP since 1999
@shochdoerfer
Testing untestable code
No excuse for writing bad code!
Not to freak out!
Testing untestable code
Testing untestable code
Creativity matters!
Testing untestable code
"There is no secret to writing tests, there are only secrets to write
testable code!" Miško Hevery
What is „untestable Code“?
Testing untestable code
s
Testing untestable code
1. Wrong object construction
“new” is evil!
Testing untestable code
2. Tight coupling
No code reuse!
Testing untestable code
No isolation → not testable!
Testing untestable code
Testing untestable code
3. Uncertainty
Testing untestable code
"...our test strategy requires us to have more control [...] of the sut."
Gerard Meszaros, xUnit Test Patterns: Refactoring Test Code
Testing untestable code
SUTSUTUnittestUnittest
In a perfect world...
Testing untestable code
SUTSUTUnittestUnittest
Legacy code is not perfect...
DependencyDependency
DependencyDependency
Testing untestable code
SUTSUTUnittestUnittest
Legacy code is not perfect...
DependencyDependency
DependencyDependency
...
...
Testing untestable code
SUTSUTUnittestUnittest
Legacy code is not perfect...
DependencyDependency
DependencyDependency
...
...
Testing untestable code
How to get „testable“ code?
Testing untestable code
How to get „testable“ code?
Refactoring
Testing untestable code
"Before you start refactoring, check that you have a solid suite of tests."
Martin Fowler, Refactoring
Testing untestable code
Hope? - Nope...
Testing untestable code
Which path to take?
Testing untestable code
Which path to take?
Do not change existing code!
Testing untestable code
Examples
Object Construction External resources Language issues
Testing untestable code
Object construction
<?phpclass Car {
private $Engine;
public function __construct($sEngine) {$this->Engine = Engine::getByType($sEngine);
}
}
Testing untestable code
Object construction - Autoload<?phpfunction run_autoload($psClass) {
$sFileToInclude = strtolower($psClass).'.php';if(strtolower($psClass) == 'engine') {
$sFileToInclude = '/custom/mocks/'. $sFileToInclude;
}include($sFileToInclude);
}
// Testcasespl_autoload_register('run_autoload');$oCar = new Car('Diesel');
Testing untestable code
Object construction
<?phpinclude('Engine.php');
class Car {private $Engine;
public function __construct($sEngine) {$this->Engine = Engine::getByType($sEngine);
}}
Testing untestable code
Object construction - include_path
<?phpini_set('include_path',
'/custom/mocks/'.PATH_SEPARATOR.ini_get('include_path'));
// Testcaseinclude('car.php');
$oCar = new Car('Diesel');echo $oCar->run();
Testing untestable code
Object construction – Stream Wrapper
<?phpclass CustomWrapper { private $_handler;
function stream_open($path, $mode, $options,&$opened_path) {
stream_wrapper_restore('file'); // @TODO: modify $path before fopen
$this->_handler = fopen($path, $mode); stream_wrapper_unregister('file'); stream_wrapper_register('file', 'CustomWrapper'); return true; }}
Testing untestable code
Object construction – Stream Wrapper
stream_wrapper_unregister('file');stream_wrapper_register('file', 'CustomWrapper');
Testing untestable code
Object construction – Stream Wrapper
<?phpclass CustomWrapper {
private $_handler;
function stream_read( $count) {$content = fread($this->_handler, $count);$content = str_replace ('Engine::getByType' ,
'AbstractEngine::get' , $content);return $content;
}}
Testing untestable code
Object construction – Namespaces
<?phpclass Car {
private $Engine;
public function __construct($sEngine) {$this->Engine = \Car\Engine::
getByType($sEngine);}
}
Testing untestable code
External resources
Testing untestable code
External resources
Database Webservice
Filesystem Mailserver
Testing untestable code
External resources – Mock database
Testing untestable code
External resources – Mock database
Provide own implementation
Testing untestable code
External resources – Mock database
ZF example:
$db = new Custom_Db_Adapter(array());Zend_Db_Table::setDefaultAdapter($db);
Testing untestable code
External resources – Mock database
PHPUnit_Extensions_Database_TestCase
Testing untestable code
External resources – Mock database
Proxy for your SQL Server
Testing untestable code
External resources – Mock webservice
Testing untestable code
External resources – Mock webservice
Provide own implementation
Testing untestable code
External resources – Mock webservice
Host redirect via /etc/hosts
Testing untestable code
External resources – Mock filesystem
Testing untestable code
External resources – Mock filesystem
<?php
// set up test environmemtvfsStream::setup('exampleDir');
// create directory in test enviromentmkdir(vfsStream::url('exampleDir').'/sample/');
// check if directory was createdecho vfsStreamWrapper::getRoot()->hasChild('sample');
Testing untestable code
External resources – Mock Mailserver
Testing untestable code
External resources – Mock Mailserver
Use fake mail server
Testing untestable code
External resources – Mock Mailserver
$ cat /etc/php5/php.ini | grep sendmail_pathsendmail_path=/usr/local/bin/logmail
$ cat /usr/local/bin/logmailcat >> /tmp/logmail.log
Testing untestable code
Dealing with language issues
Testing untestable code
Dealing with language issues
Testing your privates?
Testing untestable code
Dealing with language issues
<?phpclass CustomWrapper {
private $_handler;
function stream_read($count) {$content = fread($this->_handler, $count);$content = str_replace(
'private function', 'public function', $content );
return $content;}
Testing untestable code
Dealing with language issues
$myClass = new MyClass();
$reflectionClass = new ReflectionClass('MyClass');$reflectionMethod = $reflectionClass->
getMethod('mydemo');$reflectionMethod->setAccessible(true);$reflectionMethod->invoke($myClass);
Testing untestable code
Dealing with language issues
Overwrite internal functions?
Testing untestable code
Dealing with language issues
pecl install runkit-0.9
Testing untestable code
Dealing with language issues - Runkit
<?php
ini_set('runkit.internal_override', '1');
runkit_function_redefine('mail','','returntrue;');
?>
Testing untestable code
Dealing with language issues
pecl install funcall-0.3.0alpha
Testing untestable code
Dealing with language issues - Funcall
<?phpfunction my_func($arg1, $arg2) { return $arg1.$arg2;}
function post_cb($args,$result,$process_time) { // return custom result based on $args}
fc_add_post('my_func','post_cb');var_dump(my_func('php', 'c'));
Testing untestable code
What else?
What else?
Testing untestable code
Generative Programming
Testing untestable code
Generative Programming
ConfigurationConfiguration
Implementationcomponents
Implementationcomponents
Generatorapplication
Generatorapplication
ProductProductGeneratorGenerator
1 ... n
Testing untestable code
Generative Programming
ConfigurationConfiguration
Implementationcomponents
Implementationcomponents
Generatorapplication
Generatorapplication
GeneratorGenerator
ApplicationApplication
TestcasesTestcases
Testing untestable code
Generative Programming
A frame is a data structure for representing knowledge.
Testing untestable code
A Frame instance
<?phpclass Car {
private $Engine;
public function __construct($sEngine) {$this->Engine = <!{Factory}!>::
getByType($sEngine);}
}
Testing untestable code
The Frame controller
public class MyFrameController extendsSimpleFrameController {
public void execute(Frame frame, FeatureConfigconfig) {
if(config.hasFeature("unittest")) { frame.setSlot("Factory", "FactoryMock");}else { frame.setSlot("Factory", "EngineFactory");}
}}
Testing untestable code
Generated result - Testcase
<?phpclass Car {
private $Engine;
public function __construct($sEngine) {$this->Engine = FactoryMock::
getByType($sEngine);}
}
Testing untestable code
Generated result - Application
<?phpclass Car {
private $Engine;
public function __construct($sEngine) {$this->Engine = EngineFactory::
getByType($sEngine);}
}
Testing untestable code
What is possible?
Testing untestable code
What is possible?
Show / hide parts of the code
Testing untestable code
What is possible?
Change content of global vars!
Testing untestable code
What is possible?
Define pre- or postfixes!
Testing untestable code
Is it worth it?
Testing untestable code
Conclusions
Change your mindset to write testable code!
Testing untestable code
Conclusions
PHP is a swiss-army knife. Use it that way!
http://joind.in/3922
Testing untestable code
Bildquellen
http://www.flickr.com/photos/andresrueda/3452940751/
http://www.flickr.com/photos/andresrueda/3455410635/