Testing untestable code - DCHH

Preview:

DESCRIPTION

 

Citation preview

Testing untestable code

Testing untestable code

Über mich

Stephan Hochdörfer, bitExpert AG

Department Manager Research Labs

PHP Fanboy seit 1999

S.Hochdoerfer@bitExpert.de

@shochdoerfer

Testing untestable code

Keine Entschuldigung für schlechten Code!

Nicht ausflippen!

Testing untestable code

Testing untestable code

Kreativität zählt!

Testing untestable code

"There is no secret to writing tests, there are only secrets to write

testable code!" Miško Hevery

Was ist „untestbarer Code“?

Testing untestable code

s

Testing untestable code

1. Falsche Instantiierung

“new” is evil!

Testing untestable code

2. Starke Kopplung

Keine Wiederverwendbarkeit

Testing untestable code

Nicht isolierbar, nicht testbar!

Testing untestable code

Testing untestable code

3. Ungewissheit

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 einer perfekten Welt..

Testing untestable code

SUTSUTUnittestUnittest

Legacy code ist nicht perfekt...

AbhängigeKlasse

AbhängigeKlasse

AbhängigeKlasse

AbhängigeKlasse

Testing untestable code

SUTSUTUnittestUnittest

Legacy code ist nicht perfekt...

AbhängigeKlasse

AbhängigeKlasse

AbhängigeKlasse

AbhängigeKlasse

...

...

Testing untestable code

SUTSUTUnittestUnittest

Legacy code ist nicht perfekt...

AbhängigeKlasse

AbhängigeKlasse

AbhängigeKlasse

AbhängigeKlasse

...

...

Testing untestable code

Wie erzeugt man „testbaren Code“?

Testing untestable code

Wie erzeugt man „testbaren Code“?

Refactoring

Testing untestable code

"Before you start refactoring, check that you have a solid suite of tests."

Martin Fowler, Refactoring

Testing untestable code

Hoffnung? ...ist nicht genug

Testing untestable code

Wohin führt die Reise?

Testing untestable code

Wohin führt die Reise?

Vorhandenen Code nicht ändern!

Testing untestable code

Beispiele

Objekterzeugung Externe Ressourcen Sprachbarrieren

Testing untestable code

Objekterzeugung

<?phpclass Car {

private $engine;

public function __construct($engine) {$this->engine = Engine::getByType($engine);

}

}

Testing untestable code

Objekterzeugung - Autoload

<?phpfunction run_autoload($class) {

$fileToInclude = strtolower($class).'.php';if(strtolower($class) == 'engine') {

$fileToInclude = '/custom/mocks/'. $fileToInclude;

}include($fileToInclude);

}

// Testcasespl_autoload_register('run_autoload');$car = new Car('Diesel');echo $car->run();

Testing untestable code

Objekterzeugung

<?phpinclude('Engine.php');

class Car {private $engine;

public function __construct($engine) {$this->engine = Engine::getByType($engine);

}}

Testing untestable code

Objekterzeugung - include_path

<?phpini_set('include_path',

'/custom/mocks/'.PATH_SEPARATOR.ini_get('include_path'));

// Testcaseinclude('car.php');

$car = new Car('Diesel');echo $car->run();

Testing untestable code

Objekterzeugung – 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

Objekterzeugung – Stream Wrapper

stream_wrapper_unregister('file');stream_wrapper_register('file', 'CustomWrapper');

Testing untestable code

Objekterzeugung – 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

Objekterzeugung – Namespaces

<?phpclass Car {

private $engine;

public function __construct($engine) {$this->engine = \Car\Engine::

getByType($engine);}

}

Testing untestable code

Externe Ressourcen

Testing untestable code

Externe Ressourcen

Datenbank Webservice

Dateisystem Mailserver

Testing untestable code

Externe Ressourcen – Datenbank mocken

Testing untestable code

Externe Ressourcen – Datenbank mocken

Eigene Implementierung anbieten

Testing untestable code

Externe Ressourcen – Datenbank mocken

ZF Beispiel:

$db = new Custom_Db_Adapter(array());Zend_Db_Table::setDefaultAdapter($db);

Testing untestable code

Externe Ressourcen – Datenbank mocken

PHPUnit_Extensions_Database_TestCase

Testing untestable code

Externe Ressourcen – Datenbank mocken

Proxy for den SQL Server

Testing untestable code

Externe Ressourcen – Webservice mocken

Testing untestable code

Externe Ressourcen – Webservice mocken

Eigene Implementierung anbieten

Testing untestable code

Externe Ressourcen – Webservice mocken

Host Alias via /etc/hosts

Testing untestable code

Externe Ressourcen – Dateisytem mocken

Testing untestable code

Externe Ressourcen – Dateisystem mocken

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

Externe Ressourcen – Mailserver mocken

Testing untestable code

Externe Ressourcen – Mailserver mocken

fake mail server verwenden

Testing untestable code

Externe Ressourcen – Mailserver mocken

$ cat /etc/php5/php.ini | grep sendmail_pathsendmail_path=/usr/local/bin/logmail

$ cat /usr/local/bin/logmailcat >> /tmp/logmail.log

Testing untestable code

Sprachbarrieren

Testing untestable code

Sprachbarrieren

private Methoden testen?

Testing untestable code

Sprachbarrieren

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

Sprachbarrieren

$myClass = new MyClass();

$reflectionClass = new ReflectionClass('MyClass');$reflectionMethod = $reflectionClass->

getMethod('mydemo');$reflectionMethod->setAccessible(true);$reflectionMethod->invoke($myClass);

Testing untestable code

Sprachbarrieren

Interne Funktionen überschreiben?

Testing untestable code

Sprachbarrieren - Runkit

pecl install runkit-0.9

Testing untestable code

Sprachbarrieren - Runkit

<?php

ini_set('runkit.internal_override', '1');

runkit_function_redefine('mail','','returntrue;');

?>

Testing untestable code

Spielraum?

Testing untestable code

Abstrahieren du sollst!

Was sonst?

Testing untestable code

Generative Programmierung

Generative Programmierung

Testing untestable code

GeneratorGenerator

Generative Programmierung

Testing untestable code

Konfiguration(DSL)

Konfiguration(DSL)

GeneratorGenerator

Generative Programmierung

Software generieren statt programmieren

Konfiguration(DSL)

Konfiguration(DSL)

Implementierungs-komponenten

Implementierungs-komponenten GeneratorGenerator

Generative Programmierung

Testing untestable code

Konfiguration(DSL)

Konfiguration(DSL)

Implementierungs-komponenten

Implementierungs-komponenten GeneratorGenerator

ProduktProdukt

1..n

Generative Programmierung

Testing untestable code

Konfiguration(DSL)

Konfiguration(DSL)

Implementierungs-komponenten

Implementierungs-komponenten GeneratorGenerator

ApplikationApplikation

TestsTests

Testing untestable code

Generative Programmierung

Ein Frame ist eine Datenstrukturdie Wissen repräsentiert.

Testing untestable code

Eine Frame Instanz

<?phpclass Car {

private $engine;

public function __construct($engine) {$this->engine = <!{Factory}!>::

getByType($engine);}

}

Testing untestable code

Der 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

Nach der Generierung - Testcase

<?phpclass Car {

private $engine;

public function __construct($engine) {$this->engine = FactoryMock::

getByType($engine);}

}

Testing untestable code

Nach der Generierung - Applikation

<?phpclass Car {

private $engine;

public function __construct($engine) {$this->engine = EngineFactory::

getByType($engine);}

}

Testing untestable code

Was ist möglich?

Testing untestable code

Was ist möglich?

Extraktion von Codezeilen

Testing untestable code

Was ist möglich?

Globale Inhalte verändern

Testing untestable code

Was ist möglich?

Pre- oder Postfixes für Methoden

Testing untestable code

Wie weit sollte man gehen?

Vielen Dank!

Testing untestable code

Bildquellen

http://www.flickr.com/photos/andresrueda/3452940751/

http://www.flickr.com/photos/andresrueda/3455410635/

Recommended