Presenting TDD
TEST-DRIVEN DEVELOPMENT
2009-04-29
Brian Rasmussen
http://www.linkedin.com/in/brianrasmussen
AGENDA
Answers to the WH-words
Where to begin
The Turn
the Twist
Excuses and Common Traps
Where to go from here
(WHO WHAT WHERE)
What is TDD
WHAT IS TDD
Test-Driven Development (or test driven design) is
a methodology.
Common TDD misconception:
TDD is not about testing
TDD is about design and development
By testing first you design your code
WHAT IS TDD
Short development iterations.
Based on requirement and pre-written test cases.
Produces code necessary to pass that iteration's
test.
Refactor both code and tests.
The goal is to produce working clean code that
fulfills requirements.
TEST-DRIVEN DEVELOPMENT
Test-driven development (TDD) is a software
development technique that uses short
development iterations based on pre-written test
cases that define desired improvements or new
functions. Each iteration produces code necessary
to pass that iteration's tests. Finally, the
programmer or team refactors the code to
accommodate changes. A key TDD concept is that
preparing tests before coding facilitates rapid
feedback changes. Note that test-driven
development is a software design method, not
merely a method of testing.
HOW DOES TDD HELP
TDD helps you produce clean working code that
fulfills requirements
Write Test Code
Write Functional Code
Code that fulfills requirements
Working code that fulfills requirements
Refactor
Clean working code that fulfills requirements
ORIGIN
eXtreme Programming (XP)
1999 Kent Beck, Martin Fowler and others
TDD BASICS - UNIT TESTING
Red, Green, Refactor
Make it Fail
Make it Work
No code without a failing test
As simply as possible
Make it Better
Refactor
TDD CYCLE
Make it Better
Make it Fail
Make it Work
TDD CYCLE
Write Test Code
Guarantees that every functional code is testable
Provides a specification for the functional code
Helps to think about design
Ensure the functional code is tangible
Write Functional Code
Fulfill the requirement (test code)
Write the simplest solution that works
Leave Improvements for a later step
The code written is only designed to pass the test
no further (and therefore untested code is not created).
Refactor
Clean-up the code (test and functional)
Make sure the code expresses intent
Remove code smells
Re-think the design
Delete unnecessary code
Why TDD
WHY / BENEFITS
Confidence in change
Increase confidence in code
Fearlessly change your code
Document requirements
Discover usability issues early
Regression testing = Stable software = Quality
software
WHERE DOES IT HURT
The pain is here!
100%
This is too late
10000
9000
80%
8000
7000
60%
6000
5000
% defects created
40%
4000
3000
20%
Thousand $s
2000
1000
0%
% of Defects Introduced
Cost to Fix a Defect
IS TDD A WASTE OF TIME
(MICROSOFT RESEARCH)
160%
140%
120%
100%
80%
60%
40%
20%
0%
Time To Code Feature
Time To Code Feature
using TDD
Defect density of
team
Defect density of
team using TDD
Major quality improvement for minor time
investment
How to
REMEMBER - THERE ARE OTHER KINDS OF
TESTS
Unit test (Unit)
Integration test (Collaboration)
User interface test (Frontend, ex. WatiN)
Regression test (Continuous Integration)
, System, Performance, Stress, Usability,
The only tests relevant to TDD is Black-box Unit
Testing
Test types:
Black-box test
White-box test
DIFFICULT SCENARIOS TO UNIT TEST
Closed Object Models (Sharepoint, Silverlight)
Client server architecture
An out-of-process call
Includes talking to databases and Web Services.
UI
Communicating Across a Network
GUI interaction
Touching the File System
Legacy Code
Requires the Environment to be configured
HOW TO DO TDD
on my component A?
Unit Test A, but what about B, C, D?
HOW TO DO TDD
on my component A?
This, and the previous two slides,
all thoughts
on reference
implementing tests on existing
are
If my
component
My coworkers component code.
A third party component
A very slow component (Database, Web service)
This
is all White-box Unit- or Integration
A component with complex set up
has therefore
nothing
to do with
Aand
component
with exceptional
behavior (Exception)
A remote component (Remoting)
Circular dependency
Testing
TDD.
In TDD you write a Black-box Unit test that fails,
first,
Do I worry
have to
waitthe
on these
components to be
and
about
code implementation
later.
created and/or tested?
SINGLE MOST IMPORTANT THING
WHEN LEARNING TDD
Do not
write the code in your
head
before you write the test
SINGLE MOST IMPORTANT THING
WHEN LEARNING TDD
When you first start at doing TDD you "know" what
the design should be. You "know" what code you want
to write. So you write a test that will let you write that
bit of code.
When you do this you aren't really doing TDD since
you are writing the code first (even if the code is only
in your head )
It takes some time to (and some poking by clever folk)
to realize that you need to focus on the test. Write the
test for the behavior you want - then write the
minimal code needed to make it pass - then let the
design emerge through refactoring. Repeat until done.
Where to begin
UNBOUNDED STACK EXAMPLE
Requirement: FILO / LIFO messaging system
Brainstorm a list of tests for the requirement:
Create a Stack and verify that IsEmpty is true.
Push a single object on the Stack and verify that IsEmpty is false.
Push a single object, Pop the object, and verify that IsEmpty is true.
Push a single object, remembering what it is; Pop the object, and verify
that the two objects are equal.
Push three objects, remembering what they are; Pop each one, and verify
that they are removed in the correct order.
Pop a Stack that has no elements.
Push a single object and then call Top. Verify that IsEmpty is false.
Push a single object, remembering what it is; and then call Top. Verify that
the object that is returned is the same as the one that was pushed.
Call Top on a Stack with no elements.
UNBOUNDED STACK EXAMPLE
Choosing the First Test?
The simplest.
The essence.
Answers:
If you need to write code that is untested, choose a
simpler test.
If the essence approach takes to much time to
implement, choose a simpler test.
UNBOUNDED STACK EXAMPLE
Anticipating future tests, or not?
Answers:
In the beginning, focus on the test you are writing,
and do not think of the other tests.
As you become familiar with the technique and the
task, you con increase the size of the steps.
But remember still, no written code must be
untested.
Unbounded stack
example
UNBOUNDED STACK EXAMPLE
As we were implementing the sixed test, a few
additional tests came to mind, so we need to add
them to our test list.
We want to add tests to verify that the Stack works
when the arguments are equal to null. The new
tests are as follows:
Push null onto the Stack and verify that IsEmpty returns false.
Push null onto the Stack, Pop the Stack, and verify that the value
returned is null.
Push null onto the Stack, call Top, and verify that the value returned is
null.
The Turn
WHY DONT YOU DO TDD, STILL
Why, when TDD was born a decade ago?
The one and only reason: #1: Learning curve
So why am I telling you this today?
Something happened in the last year
TECHNIQUES YOU NEEDED TO KNOW ABOUT
Interfaces
Dependency Injection
Test Doubles (Mock Objects)
Mocking Framework.
The builder and fluent interface patterns.
Not a chance you still need to know about:
Refactoring
THE OLD WAY VS THE NEW WAY
Either (the old way)
Mock
Using Interfaces, Dependency Injection and
Test Doubles (Mock Objects)
Or (the new way)
Isolate and Fake
Using Typemock Isolator
and AAA - The "Arrange-act-assert" pattern
WHY ISOLATE AND / OR FAKE AT ALL
when you can hand-writemocks yourself.
ASP.NET MVC example
WHY ISOLATE AND / OR FAKE AT ALL
when you can hand-writemocks yourself.
ASP.NET MVC
Rewriting all this with Isolator code
AccountControllerTest file contains 25 tests.
It's a waste of time.
The mocking code starts at line 376 and goes on until line 668 (the end
ofStart
the file).
That's 293
of mocking
code, some of it
contains logic.
using
an lines
isolation
framework
now.
Ends at line 392
I'll let you do the math.
I wrote mock code like that before. Can you believe my mocking code
even contained bugs? I had to fix those as well.
WHY BUY WHEN YOU CAN GET IT FOR FREE
Typemock (Commercial) vs. other frameworks.
The "free" products are not called free, they are open-source projects.
They are built in most cases by a single person, and maintained by the
community if they are successful.
There's a lot of effort and love put into that. What's the return? There's the
great satisfaction someone finds your software useful (Hey, that also goes
for commercial products).
Ayende, creator of Rhino Mocks and Daniel, creator of Moq, put a lot of
hours into feature requests and support, but there's a limit to what they
can do.
Take a look at Ayende's slogan on his blog: "Send me a patch for
this".
Don't be a whiner. Take responsibility. Compare the good, bad and ugly.
And pick what is right for you, not because it's what you can afford, but for
what it will save you, give you back or help you outperform your
competitors.
Of course you get what you pay for (Nothing + More nothing = Nothing).
TYPEMOCK
Established 2005, privately owned
Typemock Isolator
Thousands of customers (Fortune 500)
Tens of thousands licenses sold
TYPEMOCK.NET HISTORY
The Typemock Isolator product supports three sets
of API in order to fake dependencies:
Reflective Mocks
Natural Mocks
String based API (Oldest framework).
Record-Replay but strongly typed (Old framework).
Typemock Isolator
Arrange Act Assert (AAA) Syntax (Latest API version - you are encouraged
to use this).
ARRANGE ACT ASSERT (AAA)
The Arrange-act-assert pattern in the field of mock
frameworks represents us trying to return to basics.
Moq did that nicely by showing a way that leads there,
and it is being taken gladly by Rhino and Typemock
Isolator.
AAA is guidance because there was an outcry (a
silent, vote of feet) that record-replay is not cutting it
its too confusing for non-experts. Its too technical.
and it does not lend itself to the way people want to
work. So most people didnt use it.
the Twist
Typemock Isolator
Demo
Excuses
TOP FIVE EXCUSES FOR NOT UNIT TESTING
1.
2.
3.
4.
5.
I dont have time to unit test.
The client pays me to develop code, not write unit
test.
I am supporting a legacy application without unit
tests.
QA and User Acceptance Testing is far more
effective in finding bugs.
I dont know how to unit test, or I dont know how
to write good unit tests.
WHEN DO YOU THINK YOU'LL INTRODUCE
UNIT TESTING
A common answer to this question:
If you gave me this answer, I won't believe you.
Here's why:
"We're in a middle of a project right now. We'll wait two months until it
completes, then we'll have time".
If I came to you in two months and asked what has changed since last
time, you'll give me the same answer.
That's because development is always done under
pressure.
WHEN DO YOU THINK YOU'LL INTRODUCE
UNIT TESTING
In two months time, you'll be up to your neck in
fixing bugs, running away from QA reports. You'll fix
those bugs, and eat away at the time you thought
you'd have when the project completes. And when it
does complete (late), and you think it's over, you'll
start receiving bug reports from the customer. Guess
what? no time to start unit testing then either.
Let's face it, there is never a good time to start unittesting, or making any change, for that matter. So,
start today. Write your first unit tests. Change is
painful, and you'd rather delay that. But in agile, we
say: if it's painful, do it first.
TEST CODE IS JUST CODE - COMMON TRAPS
Some Anti-Patterns to avoid and some techniques for
making sure Test Code doesn't slow you down:
Cut & Paste
Poor Encapsulation
Isolation framework / Dependency Injection and Test Doubles
Too hard to write the Test
The builder and fluent interface patterns are very useful here
Bloated SetUp
Refactor test code
Refactor production code
Code Integration Test Pain
Remember Integration Testing as well
CLASSICISTS V MOCKIST V ISOLATOR
Classicists
The classical TDD style is to use real objects if possible and a double if it's
The Classicists does White-box Integration
awkward to use the real thing. So a classical TDDer would use a real
testing
warehouse and a double for the
mail service. The kind of double doesn't
really matter that much.
First generation TDD
Mockist
A mockist TDD practitioner, however, will always use a mock for any
The Mockist does White-box Unit testing
object with interesting behavior. In this case for both the warehouse and
the mail service.
Sencond generation TDD
Isolator and Fake
An isolator
TDD practitioner,
willdoes
alwaysBlack-box
isolate or fake
anytesting
object with
The
Isolator
and Fake
Unit
interesting behavior. In this case for both the warehouse and the mail
Third generation TDD
service.
INTRODUCING BDD
Behaviour-Driven Development (BDD)
Test method names should be sentences
A simple sentence template keeps test methods focused
By Dan North
BDD is another variation on TDD that tends to use mockist testing
This sentence template The class should do something means you can only
define a test for the current class. This keeps you focused. If you find yourself
writing a test whose name doesnt fit this template, it suggests the behaviour
may belong elsewhere.
An expressive test name is helpful when a test fails
Behaviour is a more useful word than test.
Determine the next most important behaviour.
Requirements are behaviour, too.
BDD provides a ubiquitous language for analysis.
Acceptance criteria should be executable.
WHEN TO USE TDD/BDD
When you have to implement a new functional
requirement, or make a requirement up yourself.
Note: When you want to change existing code, first
write covering Unit Tests (white box testing).
Then use TDD to add new functionality.
TOOLS YOU NEED TO KNOW ABOUT
Continuous Integration
Build Automation Tools
MSBuild
Nant
FinalBuilder
CI Servers
Team System / Team Foundation Server
TeamCity (R#)
CCnet (CruiseControl.NET)
FinalBuilder Server
TOOLS YOU NEED TO KNOW ABOUT
Visual Studio Integration
Refactoring
R# (ReSharper)
Code Rush
TOOLS YOU NEED TO KNOW ABOUT
Isolation / Mocking Frameworks for .net
Open Source
Rhino Mocks for .NET
NMock for .NET
Moq for .Net
Commercial (costs money but worth it)
Typemock Isolator
Typemock Isolator For SharePoint
Where to go from here
WHERE TO GO FROM HERE
You dont have to start big
Start new tasks with TDD
Add Tests to code that you need to change or
maintain but only to small parts
Proof of concept
If it's worth building, it's worth testing.
If it's not worth testing,
why are you wasting your time working on
it?
LINKS
Books
Blog
http://www.typemock.com/
Unit testing Silverlight:
http://research.microsoft.com/en-us/projects/esm/nagappan_tdd.pdf
Typemock
The Typemock Insider Blog (http://blog.typemock.com/)
Unit test study from Microsoft Research:
Test-Driven Development in Microsoft .NET (http://www.amazon.co.uk/gp/product/0735619484)
Test-Driven Development by Kent Beck (C++) (http://www.amazon.co.uk/gp/product/0321146530)
http://www.codeplex.com/CThru
Other links:
http://en.wikipedia.org/wiki/Test-driven_development
http://www.testdriven.com/
http://www.mockobjects.com/ - Online TDD book: http://www.mockobjects.com/book/index.html
http://www.martinfowler.com/articles/mocksArentStubs.html
http://dannorth.net/introducing-bdd
http://behaviour-driven.org/Introduction
Q&A