28
Unit Testing für Dummies 15.11.2009 Lars Jankowfsky, swoodoo AG Thorsten Rinne, Mayflower GmbH

Unittests für Dummies

Embed Size (px)

DESCRIPTION

Refactoring, Agile Entwicklung, Continuous Integration – all diese für nachhaltigen Erfolg wichtigen Vorgehensweisen setzen Erfahrung mit Unit Testing voraus. Abseits von den üblichen "Bowling"-Beispielen möchten wir gerne einen Crashkurs inkl. Best Practices für das erfolgreiche Unit Testing durchführen. Anhand eines Beispielprojekts auf Basis des Zend Frameworks werden wir nach der Installation von PHPUnit auf allen Notebooks gemeinsam eine kleine Applikation aufbauen, die durchgehend Test-driven entwickelt wird.

Citation preview

Page 1: Unittests für Dummies

Unit Testing für Dummies

15.11.2009

Lars Jankowfsky, swoodoo AGThorsten Rinne, Mayflower GmbH

Page 2: Unittests für Dummies

About me:

PHP, C++, Developer, Software Architect since 1992

PHP since 1998

Many successful projects from 2 to 20 developers

Running right now three projects using eXtreme Programming

CTO and (Co-)Founder swoodoo AG

(Co-)Founder OXID eSales AG

Page 3: Unittests für Dummies

About me:

Diplom-Informatiker (FH) Certified Scrum MasterZend Certified Engineer

PHP since 1999

Many successful projects from 2 to 6 developers using Scrum, eXtreme Programming, Crystal Clear

Senior Developer / Team Lead at Mayflower GmbH

Master of phpMyFAQ

Page 4: Unittests für Dummies

(c) aboutpixel.de(c) spackletoe http://www.flickr.com/photos/spackletoe/90811910/)

Page 5: Unittests für Dummies

Unit Tests?

PHPUnit Fixtures

Stubs Pitfalls

Page 7: Unittests für Dummies

PHPUnit

phpunit myTest

user@workshop:/var/www/thorsten-zfguestbook/tests/application$ phpunit controllers_PostsControllerTest

PHPUnit

Page 8: Unittests für Dummies

Fixtures

Page 9: Unittests für Dummies

Fixtures Fixtures

Make sure that tests don‘t alter fixture

Fixture is FIXture

if you feel creating fixtures is too much work - refactor more!

Do never let tests leave altered tests

Page 11: Unittests für Dummies

YAML loading

public static function create($fileName) { $fileName = 'Fixtures'.DIRECTORY_SEPARATOR.$fileName; ob_start(); include $fileName; $fileContents = ob_get_contents(); ob_clean(); $yamlData = syck_load($fileContents); return $yamlData; }

Fixtures

Page 12: Unittests für Dummies

YAML storing

public static function load($fixtures, $tableName) { if (is_array($fixtures) && count($fixtures)) { foreach ($fixtures as $fixture) { if (is_array($fixture) && is_array(current($fixture))) { Fixtures::load($fixture, $tableName); } $fields = array_keys($fixture); $statement = "INSERT INTO $tableName (" . implode(', ', $fields) . ") VALUES (:" . implode(", :", $fields) . ")"; $stmt = self::$_db->prepare($statement); if (count($fixture)) { foreach ($fixture as $key => $value ) { $stmt->bindValue(':'.$key, $value); } } $stmt->execute(); self::$_usedTables[$tableName] = $tableName; } } }

Fixtures

Page 13: Unittests für Dummies

YAML - cleanup

if (!empty(self::$_usedTables)) { foreach (array_reverse(self::$_usedTables) as $tableName) { self::$_db->execute("TRUNCATE TABLE $tableName"); } }

Fixtures

Page 14: Unittests für Dummies

Fixtures the other side ...

manual fixtures are too much work

use a test database

think about automatic creation of YAML files

Fixtures

Page 15: Unittests für Dummies

Stubs

Page 16: Unittests für Dummies

Mocking stubs? Stubs

Unittesting is about testing a unit of work, not a complete workflow

isolates your code from external dependencies

can be done with PHPUnit, but you don‘t need to

Page 17: Unittests für Dummies

Mocking stubs The PHPUnit way Stubs

/** * A simple stub providing a simple result directly instead of using the database */class UserModelStub extends UserModel { public getUserCount() { return 10; }}

UserModelStub extends PHPUnit_Framework_Testcase { public function testGetUserCount() { $stub = $this->getMock(‘UserModel‘); $stub->expects($this->any())->method(‘getUserCount‘)->will($this->returnValue(10)); }}

Page 18: Unittests für Dummies

Pitfalls

Page 19: Unittests für Dummies

Code the unit test first. Pitfalls

OOP, public, private

Globals

Superglobals

Sessions

Cookies

Page 20: Unittests für Dummies

Dependencies ...

Separate logic from view

create accessors, add all parameters in calls

Pitfalls

Page 21: Unittests für Dummies

Dependency Example

class displayUserDetails(){ /** * Processes input and sends user first name, last name to display; */ function show() { global $dbLink; global $templateEngine; $itemId = (int) $_REQUEST['user_id']; $firstName = $dbLink->getOne("select first_name from users where id = $itemId"); $lastName = $dbLink->getOne("select last_name from users where id = $itemId"); $templateEngine->addTemplateVar('firstName', $firstName); $templateEngine->addTemplateVar('lastName', $lastName); $templateEngine->display(); }}

Pitfalls

Page 22: Unittests für Dummies

Dependency Example

/** * A view class responsible for displaying user details. */class userView(){ /** * Loads user object and sends first name, last name to display */ public function show() { $userId = $this->_inputProcessor->getParameter("user_id"); $this->templateEngine->addTemplateVar('user', $this->model->loadUser(userId)); $this->templateEngine->display(); }} /** * And the corresponding model */class userModel(){ public function loadUser($userId) { $user = new User( $userId ); return array('firstName' => $user->getFirstName(), 'lastName' => $user->getLastName()); }}

Pitfalls

Page 23: Unittests für Dummies

STOP

Page 24: Unittests für Dummies
Page 25: Unittests für Dummies

class someOtherClass { var $setting;

function calculateSomething($a, $b) { return $a+$b; }}

class myOldNastyClass {

function needToTestThisFunction() {

$class = new someOtherClass();

$z = $_GET['input'];

// ....

return $class->calculateSomething( $class->setting, $z); }}

Layer Example Pitfalls

Page 26: Unittests für Dummies

Layer Example

class someOtherClass { private $setting;

public function calculateSomething($a, $b) { return $a+$b; }

public function setSetting($set) { $this->setting = $set; }

public function getSetting() { return $this->setting; }}

class myInput { public function getParameter($name) { return $_GET[$name]; }}

class myOldNastyClass {

private $input; // set e.g. in constructor

public function needToTestThisFunction(someOtherClass &$class, $z) {

$z = $input->getParameter('input'); // ....

return $class->calculateSomething( $class->getSetting(), $z); }}

Pitfalls

Page 27: Unittests für Dummies

(c) istockphoto

Page 28: Unittests für Dummies

„Questions?“