testing untestable code - phpday

69
Testing untestable code Stephan Hochdörfer, bitExpert AG

Upload: stephan-hochdoerfer

Post on 10-May-2015

1.601 views

Category:

Documents


2 download

TRANSCRIPT

Testing untestable codeStephan Hochdörfer, bitExpert AG

Testing untestable code

About me

Stephan Hochdörfer, bitExpert AG

Department Manager Research Labs

enjoying PHP since 1999

[email protected]

@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

Wrong object construction

“new” is evil!

Testing untestable code

Tight coupling

Testing untestable code

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

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/3026