This document discusses test driven development and how to write testable code. It recommends writing tests before writing code to prevent "zombie code" that is hard to maintain and change. Specific tips provided include using dependency injection, following SOLID principles to separate concerns, and writing fast, isolated tests using tools like PHPUnit and PHPSpec. Continuous integration is also recommended to prevent technical debt from accumulating.
3. Why do we write code?
● Add New Features
● Maintaining old features
4. What are the challenges?
● I can’t understand what the code does
● Only the original programmer understands what it does… He’s not
with us anymore
● Fixing a Bug creates a NEW BUG
● There is no documentation
● It’s Only working on my local
● We can’t upgrade because all are code is dependent on this
version
● The only way to test is with the real data on production
● I have to test this manually
5. What are we really saying?
“I’m Scared...When I change something, some
other feature might stop working”
7. How to survive the Zombie apocalypse
1. Write TESTS!
2. Write TESTABLE CODE
8. The Power of Testable code
●
●
●
●
●
●
●
●
●
●
Forces Simplification of the code
Simplifies Design
Documentation of the code
Less time debugging
New code doesn’t break old code
Refactoring becomes easier
Interfaces are better designed
Easier to do code reviews
Fearless programming
Faster than writing code without
tests
9. How to write testable code
PHPUnit
Database Testing
Legacy code testing
PHPspec
10. Why aren’t we testing?
●
●
●
●
●
●
●
●
Testing main flow is enough
The code is legacy, impossible to test
Don’t have time to test
Fix bug first then write a test
To be on the safe side manual testing is mandatory
No one else is writing tests
Testing and maintaining tests will cost more time
After changing code, I have to change a bunch of test
14. HOW TO GET STARTED?
“If the answer is not obvious, or if the tests looks like the
tests would be ugly or had to write, then take that as a
warning signal.
Your design probably needs to modified; change things
around until the code is easy to test, and your design will
end up being better for the effort”
15. How can you tell if your code isn’t going to
come back from the SVN and BYTE you in the
APP?
Look for the following SYMPTOMS.
16. SYMPTOMS - CONSTRUCTOR DOES TOO MUCH
●
●
●
●
●
●
Using the NEW keyword in the constructor
STATIC METHOD calls
Anything more than field assignment
Object not fully initialized after constructor
CONTROL FLOW (conditions or loops) in constructor
Constructor build complex Collections instead of using
FACTORY or BUILDER
● There is an INITIALIZE block
● Not asking for Objects, looking for Objects
(DEPENDENCIES in constructor)
17. SYMPTOMS - Digging into dependencies
● Object passed are used to access other objects
● LAW OF DEMETER VIOLATION: Method call chain
with more than one ->
● SUSPICIOUS NAMES: Manager, Context, Registry,
Container
● Creating mocks that return MOCKS
● Deceitful API (Real Dependencies are unclear)
● Too many “Middle Men” objects
● Needle in a haystack (due to breaking demeter law)
18. SYMPTOMS - Singletons & Global state (the dark side)
●
●
●
●
●
●
●
●
Using SINGLETONS
Accessing GLOBAL STATE STATICALLY
Using STATIC FIELDS or STATIC METHODS
Using a STATIC INITIALIZATION block
Using a REGISTRIES
NOT (or MIS-) using DEPENDENCY INJECTION
Hard Coded Dependencies
You have to read EVERY LINE OF CODE to
understand the potential side effects
20. SYMPTOMS - Object does too much
● When you have to use the word “AND” to describe
what the Object does
● Object is hard to read for team members
● Object have fields that are only used in some methods
● Object has static methods that only operate on
parameters
● MANY collaborators that you need to reach into for
more collaborators
● HIDDEN INTERACTIONS behind public methods
22. Tests should...
● Run Without
○ Network connection
○ Production Data
○ External API
● Run Fast
○ ~milliseconds
● Must
○ be Easy to maintain
○ run before every
commit
23. Rules for TDD
1. Devise possible tests
2. Implement 1 FAILING
test
3. Implement just enough
code to PASS the test
4. Refactor code and clean
up design
5. Repeat until ALL tests
are passed
THINK DIFFERENTLY
TEST
DESIGN
IMPLEMENT
TEST
24. How to write testable code
PHPUnit
Database Testing
Legacy code testing
PHPspec
27. PHPUnit - Writing Test
● Use the skeleton generator (should be in Eclipse)
phpunit-skelgen --bootstrap bootstrap.php --test -- Class_Something Lib/Class/Something.php Class_SomethingTest
Tests/Lib/Class/SomethingTest.php
● Write it yourself
class Class_SomethingTest extends PHPUnit_Framework_TestCase
28. PHPUnit - Setup
●
●
●
setUpBeforeClass() - Executed before class is instantiated
○ Setup DB
○ Setup Files
setUp() - Executed before every test
○ Reset DB
○ Load Extra fixtures
tearDown() - Executed after Test
○ Clean up DB
○ Remove Files
31. Stubs and Mocks
Stubs - Test double that
allows you to control the flow
of data
Mock - Test double that
allows you to control the flow
of data and that assert that
the object was called in a
specific way
32. Test Doubles
Doubles - When you
cannot replace a
static dependency
you can preload a test
doubles instead
33. Test Doubles
Doubles - When you
cannot replace a
static dependency
you can preload a test
doubles instead
Run in own process
Preload Test Doubles
34. How to write testable code
PHPUnit
Database Testing
Legacy code testing
PHPspec
35. Database testing
●
●
●
●
Must be fast
Need to control the data in the database
Must be ran in TESTING environment
Clean up after yourself
36. 1. Setup - DB
Connection
Make sure you’re using
TEST DB
Use MYISAM to bypass
foreign key issues
Create the tables
42. How to write testable code
PHPUnit
Database Testing
Legacy code testing
PHPspec
43.
44. "Behavior is the most important thing about
software. It is what users depends on. Users
like it when we add behavior, but if we change
or remove behavior they depend on, they stop
trusting us."
45. Legacy Code Change Algorithm
1.
2.
3.
4.
5.
Identify the change points
Find test points
Break Dependencies
Write Tests
Makes changes and refactor
47. Bridge Class
● Class that bridges the gap between legacy code and
new architecture
● Allows you to test in isolation
● Helps refactor out dependencies
50. How to write testable code
PHPUnit
Database Testing
Legacy code testing
PHPspec
51. PHPSpec
●
http://www.phpspec.net
While PHPUnit focuses on testing the functions and methods of the classes
used to develop features, specBDD focuses on testing the behaviors of these
classes.
“ describing the code before you actually write it is a fear management
technique. You don’t have to write all the code, just the spec of the next thing
you want to work on. ” -- Kent Beck
53. It’s not worth writing test unless you have
CONTINUOUS INTEGRATION
I
ZOMBIES
54. The broken window Theory
Don't leave "broken windows" (bad designs, wrong decisions, or poor code)
unrepaired. Fix each one as soon as it is discovered. Take some action to
prevent further damage and to show that you're on top of the situation.
We've seen clean, functional systems deteriorate pretty quickly once builds
start breaking. There are other factors that can contribute to software rot, but
neglect accelerates the rot faster than any other factor.