Getting started with TDD - Confoo 2014

Preview:

DESCRIPTION

Introduction to Test Driven Development. I gave that talk on February 2014 at Confoo.

Citation preview

Getting Started With TDDEric Hogue - @ehogue

Confoo - 2014-02-27

TDD

Where should I Start?

1. Unit tests2. Test Driven Development3. What’s next?

Unit Tests

Unit Test

a method by which individual units of source code [...] are tested to determine if they are fit for use

http://en.wikipedia.org/wiki/Unit_testing

Don’t Cross boundaries

Tools

● SimpleTest● atoum● PHPT● PHPUnit

Installation - Phar

$ wget

https://phar.phpunit.de/phpunit.phar

$ chmod +x phpunit.phar

$ mv phpunit.phar /usr/local/bin/phpunit

Installation - Pear

pear config-set auto_discover 1

pear install pear.phpunit.de/PHPUnit

Installation - Composer

# composer.json

{

"require-dev": {

"phpunit/phpunit": "3.7.*"

}

}

$ composer install

PHPUnit

FactorialTest.php

<?php

class FactorialTest extends

\PHPUnit_Framework_TestCase {

}

public function testSomething() {

}

/** @test */

public function somethingElse() {

}

● Arrange● Act● Assert

Arrange

/** @test */

public function factOf1() {

$factorial = new Factorial;

}

Act

/** @test */

public function factOf1() {

$factorial = new Factorial;

$result = $factorial->fact(1);

}

Assert

/** @test */

public function factOf1() {

$factorial = new Factorial;

$result = $factorial->fact(1);

$this->assertSame(1, $result);

}

PHPUnit Assertions

● $this->assertTrue();● $this->assertEquals();● $this->assertSame();● $this->assertContains();● $this->assertNull();● $this->assertRegExp();● ...

Preparing For Your Tests

setup() -> Before every teststeardown() -> After every tests

setUpBeforeClass() + tearDownAfterClass()Once per test case

phpunit.xml

<phpunit bootstrap="bootstrap.php"

colors="true"

strict="true"

verbose="true"

>

...

</phpunit>

phpunit.xml

<phpunit>

<testsuites>

<testsuite name="My Test Suite">

<directory>path</directory>

<file>path</file>

<exclude>path</exclude>

</testsuite>

</testsuites>

</phpunit>

TDD

Red - Green - Refactor

Red Write a failing test

Red - Green - Refactor

GreenMake it pass

Red - Green - Refactor

RefactorFix any shortcuts you took

/** @test */

public function create() {

$this->assertNotNull(new Factorial);

}

class Factorial {

}

/** @test */

public function factOf1() {

$facto = new Factorial;

$this->assertSame(1,

$facto->fact(1));

}

public function fact($number) {

return 1;

}

Duplication

public function create() {

$this->assertNotNull(new Factorial);

}

public function factOf1() {

$facto = new Factorial;

...

public function setup() {

$this->facto = new Factorial;

}

/** @test */

public function factOf1() {

$this->assertSame(1,

$this->facto->fact(1));

}

/** @test */

public function factOf2() {

$this->assertSame(2,

$this->facto->fact(2));

}

public function fact($number) {

return $number;

}

More duplication/** @test */

public function factOf1() {

$this->assertSame(1,

$this->facto->fact(1));

}

/** @test */

public function factOf2() {

$this->assertSame(2,

$this->facto->fact(2));

}

public function factDataProvider() {

return array(

array(1, 1),

array(2, 2),

);

}

/**

* @test

* @dataProvider factDataProvider

*/

public function factorial($number,

$expected) {

...

…$result =

$this->facto->fact($number);

$this->assertSame($expected,

$result);

}

public function factDataProvider() {

…array(2, 2),

array(3, 6),

...

public function fact($number) {

if ($number < 2) return 1;

return $number *

$this->fact($number - 1);

}

It’s a lot of work

Dependencies

Problems

class Foo {

public function __construct() {

$this->bar = new Bar;

}

}

Dependency Injection

Setter Injection

class Foo {

public function setBar(Bar $bar) {

$this->bar = $bar;

}

public function doSomething() {

// Use $this->bar

}

}

Constructor Injection

class Foo {

public function __construct(

Bar $bar) {

$this->bar = $bar;

}

public function doSomething() {

// Use $this->bar

}

}

Pass the dependency directly

class Foo {

public function doSomething(

Bar $bar) {

// Use $bar

}

}

File System

vfsStream

Virtual Files System

composer.json

"require-dev": {

"mikey179/vfsStream": "*"

},

Check if a folder was created

$root = vfsStream::setup('dir');

$parentDir = $root->url('dir');

//Code creating sub folder

$SUT->createDir($parentDir, 'test');

$this->assertTrue(

$root->hasChild('test'));

Reading a file

$struct = array(

'subDir' => array('test.txt'

=> 'content')

);

$root = vfsStream::setup('root',

null, $struct);

$parentDir = $root->url('root');

...

Reading a file

$content = file_get_contents(

$parentDir . '/subDir/test.txt');

$this->assertSame('content',

$content);

Databases

Mocks

Replaces a dependency

● PHPUnit mocks● Mockery● Phake

Creation

$mock = $this->getMock('\NS\Class');

Creation

$mock = $this->getMock('\NS\Class');

Or

$mock = $this->getMockBuilder

('\Namespace\Class')

->disableOriginalConstructor()

->getMock();

$mock->expects($this->once())

->method('methodName')

$mock->expects($this->once())

->method('methodName')

->with(1, 'aa', $this->anything())

$mock->expects($this->once())

->method('methodName')

->with(1, 'aa', $this->anything())

->will($this->returnValue(10));

Mocking PDO

$statement = $this->getMockBuilder

('\PDOStatement')

->getMock();

$statement->expects($this->once())

->method('execute')

->will($this->returnValue(true));

...

...

$statement->expects($this->once())

->method('fetchAll')

->will(

$this->returnValue(

array(array('id' => 123))

)

);

...

$this->getMockBuilder('\PDO')

->getMock();

…$pdo = $this->getMockBuilder(

'\stdClass')

->setMethods(array('prepare'))

->getMock();

$pdo->expects($this->once())

->method('prepare')

->will(

$this->returnValue($statement));

class PDOMock extends \PDO {

public function __construct() {}

}

$pdo = $this->getMockBuilder

('\PDOMock')

->getMock();

mysql_*

DbUnit Extension

extends PHPUnit_Extensions_Database_TestCase

public function getConnection() {

$pdo = new PDO('sqlite::memory:');

return $this->

createDefaultDBConnection(

$pdo, ':memory:');

}

public function getDataSet() {

return $this->

createFlatXMLDataSet('file');

}

API

● Wrap all call into a class○ Zend\Http○ Guzzle○ Simple class that uses curl

● Mock the class○ Return the wanted xml/json

Pros and Cons

Pros

● Less regressions

Pros

● Less regressions● Trust

Pros

● Less regressions● Trust● Low coupling

Pros

● Less regressions● Trust● Low coupling● Simple Design

Cons

● Takes longer

“If it doesn't have to work, I can get it done a lot faster!”- Kent Beck

Cons

● Takes longer● Can be hard to sell to managers

Cons

● Takes longer● Can be hard to sell to managers● It’s hard

Prochaines étapes?

Continuous Testing - Guard

Continuous Integration

Continuous Integration

● Run your tests automatically ○ Unit Tests○ Acceptance Tests○ Performance Tests○ ...

Continuous Integration

● Run your tests automatically ○ Unit Tests○ Acceptance Tests○ Performance Tests○ …

● Check Standards○ phpcs

Continuous Integration

● Run your tests automatically ○ Unit Tests○ Acceptance Tests○ Performance Tests○ …

● Check Standards○ phpcs

● Check for "code smells"○ phpcpd○ PHP Depend○ PHP Mess Detector

Twitter:@ehogue

Blog:http://erichogue.ca/

Slides: http://www.slideshare.net/EricHogue

Questions

Credits● Paul - http://www.flickr.com/photos/pauldc/4626637600/in/photostream/● JaseMan - http://www.flickr.com/photos/bargas/3695903512/● mt 23 - http://www.flickr.com/photos/32961941@N03/3166085824/● Adam Melancon - http://www.flickr.com/photos/melancon/348974082/● Zhent_ - http://www.flickr.com/photos/zhent/574472488/in/faves-96579472@N07/● Ryan Vettese - http://www.flickr.com/photos/rvettese/383453435/● shindoverse - http://www.flickr.com/photos/shindotv/3835363999/● Eliot Phillips - http://www.flickr.com/photos/hackaday/5553713944/● World Bank Photo Collection - http://www.flickr.com/photos/worldbank/8262750458/● Steven Depolo - http://www.flickr.com/photos/stevendepolo/3021193208/● Deborah Austin - http://www.flickr.com/photos/littledebbie11/4687828358/● tec_estromberg - http://www.flickr.com/photos/92334668@N07/11122773785/● nyuhuhuu - http://www.flickr.com/photos/nyuhuhuu/4442144329/● Damián Navas - http://www.flickr.com/photos/wingedwolf/5471047557/● Improve It - http://www.flickr.com/photos/improveit/1573943815/