Making Friends with TDD
John ClearyLead Web Developer, DocNet
@TheRealBifter
So, What is TDD?
10 Write one test20 Run tests (they fail)30 Write code to make test pass40 Run tests again (they pass)50 Refactor (code AND tests)60 Run test (they still pass)70 Goto 10
Red
GreenRefactor
Red
GreenRefactor
Write����������� ������������������ a����������� ������������������ failing����������� ������������������ test
Red
GreenRefactor
Write����������� ������������������ a����������� ������������������ failing����������� ������������������ test
Fix����������� ������������������ the����������� ������������������ test
Red
GreenRefactor
Write����������� ������������������ a����������� ������������������ failing����������� ������������������ test
Fix����������� ������������������ the����������� ������������������ test
Make����������� ������������������ it����������� ������������������ pretty
Why do it?
1. Improved Code Design
Classes that are easy to test are by definition decoupled.
1. Improved Code Design
Classes that are easy to test are by definition decoupled.
Encourages refactoring
1. Improved Code Design
2. Verification
Fewer bugs
2. Verification
Fewer bugs
Easier to maintain
2. Verification
Awesome - let’s do it, right?
Awesome - let’s do it, right?
Wait!
Does it take longer?
Does it take longer?
Maybe, maybe not.
Is it harder?
Is it harder?
For sure!
But it wrecks my creativity, dude?
But it wrecks my creativity, dude?
Really? Do some prototypes man!
Could we get these benefits without TDD?
Could we get these benefits without TDD?
No, but yeah, but no...
Awesome, let's do it!(for real this time)
Recently Used List Class
Recently Used List ClassDevelop a recently-used-list class to hold strings uniquely in Last-In-First-Out order.
Recently Used List Class
A recently-used-list is initially empty.
Develop a recently-used-list class to hold strings uniquely in Last-In-First-Out order.
Recently Used List Class
A recently-used-list is initially empty.The most recently added item is first, the least recently added item is last.
Develop a recently-used-list class to hold strings uniquely in Last-In-First-Out order.
Recently Used List Class
A recently-used-list is initially empty.The most recently added item is first, the least recently added item is last.Items can be looked up by index, which counts from zero.
Develop a recently-used-list class to hold strings uniquely in Last-In-First-Out order.
Recently Used List Class
A recently-used-list is initially empty.The most recently added item is first, the least recently added item is last.Items can be looked up by index, which counts from zero.Items in the list are unique, so duplicate insertions are moved rather than added.
Develop a recently-used-list class to hold strings uniquely in Last-In-First-Out order.
Recently Used List Class
A recently-used-list is initially empty.The most recently added item is first, the least recently added item is last.Items can be looked up by index, which counts from zero.Items in the list are unique, so duplicate insertions are moved rather than added.
Develop a recently-used-list class to hold strings uniquely in Last-In-First-Out order.
Recently Used List Class
A recently-used-list is initially empty.The most recently added item is first, the least recently added item is last.Items can be looked up by index, which counts from zero.Items in the list are unique, so duplicate insertions are moved rather than added.
Develop a recently-used-list class to hold strings uniquely in Last-In-First-Out order.
Recently Used List Class
A recently-used-list is initially empty.The most recently added item is first, the least recently added item is last.Items can be looked up by index, which counts from zero.Items in the list are unique, so duplicate insertions are moved rather than added.
Develop a recently-used-list class to hold strings uniquely in Last-In-First-Out order.
Recently Used List Class
A recently-used-list is initially empty.The most recently added item is first, the least recently added item is last.Items can be looked up by index, which counts from zero.Items in the list are unique, so duplicate insertions are moved rather than added.
Develop a recently-used-list class to hold strings uniquely in Last-In-First-Out order.
Refactor����������� ������������������ to����������� ������������������ this
That wasn’t so hard...
That wasn’t so hard...
...but what about testing the persistence layer?
You’ve got options
Mock the database layer
DBUnit
Codeception
You’ve got options
Mock the database layer
DBUnit
Codeception
You’ve got options
The����������� ������������������ purist����������� ������������������ way
Mock the database layer
DBUnit
Codeception
You’ve got options
The����������� ������������������ purist����������� ������������������ way
The����������� ������������������ easy����������� ������������������ way
Mock the database layer
DBUnit
Codeception
You’ve got options
The����������� ������������������ purist����������� ������������������ way
The����������� ������������������ easy����������� ������������������ way
The����������� ������������������ even����������� ������������������ easier����������� ������������������ way
Mock Examplefunction testInsertDatabase(){ $insertArray = array( 'name' => 'Miles', );
// create a mock database $mock = $this->getMockBuilder('Zend_Db_Adapter_Pdo_Mysql', array('insert')) ->disableOriginalConstructor() ->getMock();
// mock out the insert method $mock ->expects($this->once()) ->method('insert') ->with($insertArray);
// pass the database to the container $container = new DIContainer(): $container->set('database', $mock);
// run the test $user = new User(); $user->setName('Miles'); $user->save();}
Not great because• Requires Dependency Injection• Can be fragile• Complicated
But good because• Super fast• Not tied to database structure
DBUnit Examplepublic function testCanCreateNewUser(){ $user = new User(); $user->setName('Miles'); $user->save();
$queryTable = $this->getConnection()->createTableQuery( 'users', 'SELECT * from users' );
$expectedTable = $this->createFlatXmlDataSet("users-expected.xml") ->getTable('users');
$this->assertTableEquals($expectedTable, $queryTable);}
Not great because• Slow• Can be fragile• Tied to database structure• Requires xml setup of expected results
But good because• Tests are easy to write and read• It’s obvious whats being tested
Codeception Example<?phpfunction testSavingUser(){ $user = new User(); $user->setName('Miles'); $user->save(); $this->codeGuy->seeInDatabase('users',array('name' => 'Miles'));}?> Not great because
• Slow• Tied to database structure
But good because• Less fragile than DBUnit (no xml to maintain)• Tests are easy to write and read• It’s obvious whats being tested
When testing the DB...
When testing the DB...
Give each developer their own test database (preferably on their own VM)
When testing the DB...
Give each developer their own test database (preferably on their own VM)
DB tests are slow - move them outside the main suite or use memory tables
When testing the DB...
Give each developer their own test database (preferably on their own VM)
DB tests are slow - move them outside the main suite or use memory tables
Use a tool like “Migrations” to keep databases in sync
UI Testing
UI Testing
PHPUnit with Selenium
UI Testing
PHPUnit with Selenium
Codeception
So, where do we go from here?
2. Build up slowly
2. Build up slowly
Start with one afternoon per week
2. Build up slowly
Start with one afternoon per week
Try pair programming
3. Motivate your team
3. Motivate your team
Start a Coding Club / Kata
3. Motivate your team
Start a Coding Club / Kata
[Get your boss to] buy some books
3. Motivate your team
Start a Coding Club / Kata
[Get your boss to] buy some books
See����������� ������������������ me����������� ������������������ after����������� ������������������ this����������� ������������������ talk
4. Get a CI server
Jenkins
Travis
CruiseControl
5. Share
5. Share
Attend public Katas
5. Share
Attend public Katas
Forums & IRC
Questions ?
Thank YouJohn Cleary
Lead Web Developer, DocNet@TheRealBifter