testing untestable code - oscon 2012

Post on 13-Jan-2015

804 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

 

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

S.Hochdoerfer@bitExpert.de

@shochdoerfer

Testing untestable code

No excuse for writing bad code!

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

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

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');echo $oCar­>run();

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 Wrapperstream_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

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','','return true;');

?>

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

Dealing with language issues

funcall for methods?

Testing untestable code

Dealing with language issues

git clone https://github/juliens/AOP

Testing untestable code

Dealing with language issues - AOP

<?php

aop_add_after('Car::drive*', 'adviceForDrive');

Testing untestable code

Dealing with language issues - AOP<?php

$advice = function(AopTriggeredJoinpoint$jp) {  $returnValue =      $jp­>getReturnedValue();

  // modify the return value  $returnValue = 1234;

  $jp­>setReturnedValue($returnValue);};

aop_add_after('Car­>drive()', $advice);

What else?

Testing untestable code

Generative Programming

Testing untestable code

Generative Programming

Configuration(DSL)

Configuration(DSL)

Implementation-components

Implementation-components GeneratorGenerator

ProductProduct

1..n

Testing untestable code

Generative Programming

Configuration(DSL)

Configuration(DSL)

Implementation-components

Implementation-components GeneratorGenerator

Customer 1Customer 1

Customer 2Customer 2

Testing untestable code

Generative Programming

Configuration(DSL)

Configuration(DSL)

Implementation-components

Implementation-components GeneratorGenerator Prod.

Enviroment

Prod.Enviroment

TestEnviroment

TestEnviroment

Testing untestable code

Generative Programming

A frame is a data structure for representing knowledge.

Testing untestable code

Frame<?phpclass Car {

private $Engine;

public function __construct($sEngine) {$this­>Engine = <!{Factory}!>::

getByType($sEngine);}

}

Testing untestable code

ContentProvider for the Framepublic class MyContentProvider extends     AbstractContentProvider {    public SlotConfiguration computeSlots(        FeatureConfiguration config) {        SlotConfiguration sl = new SlotConfiguration();

        if(config.hasFeature("unittest")) {            sl.put("Factory", "FactoryMock");        } else {            sl.put("Factory", "EngineFactory");        }        return sl;    }}

Testing untestable code

Generated result – Test Enviroment<?phpclass Car {

private $Engine;

public function __construct($sEngine) {$this­>Engine = FactoryMock::

getByType($sEngine);}

}

Testing untestable code

Generated result – Prod. Enviroment<?phpclass Car {

private $Engine;

public function __construct($sEngine) {$this­>Engine = EngineFactory::

getByType($sEngine);}

}

Curious for more?

Testing untestable code

http://replicatorframework.org

Thank you!

Please rate: http://bit.ly/ML0alS

top related