tdd and refactoring
DESCRIPTION
Ganesan Rajamani's presentation on Test Driven Development and Refactoring. Was presented at the Agile Goa 2008 conference.TRANSCRIPT
smart.proven.bankable.
Confidential
TDD and RefactoringGanesan RSr.Software DeveloperSabre Holdings
Confidential 2
Game Plan
• Introduction (A Little Bit of Theory)• TDD Basics• Guidelines for Testable Design• Refactoring and its Techniques
smart.proven.bankable.
Confidential
Introduction
Confidential
Confidential 4
What is TDD?
• A Simple Definition• Write a Failing (Automated) Test Before Writing Production Code• Make a Minimal Change to Production Code to Fix the Failing Test• When There are Enough Tests, Re-factor the code
– Primary Goals: Simplicity, Readability & No Duplication
– Reliance Upon Automated Test Safety Net
– Both Production & Test Code
• Two Primary Goals of Writing Tests• Safety Net• Documentation
Confidential 5
Testing Terminology
Production Code
Unit Tests
Integration Tests
FixturesAcceptance Tests
ATDD TDD
Programmers’ TestsCustomer’s Tests
Confidential 6
TDD: The Micro-Cycle of Agile Development Process
ATDD
TDD TDD TDD TDD
Story
Task Task Task Task
“Build The Right System”
“Build It Right”
Macro
Strategic
Micro Tactical
Confidential 7
TDD: The Micro-Cycle of Agile Development Process
Release
Iteration
Day
Update View
Full Build
Check In
Green Bar
Green Bar
TDD Session
TDD Session
... IntegrateTDD
Session
Time
smart.proven.bankable.
Confidential
TDD Basics
Confidential
Confidential 9
TDD Basics (Discussion & Demo)
• Good Test is Atomic & Isolated • Decomposing Requirements Into a Test List• Programming by Intention
• Making the Compiler Happy• Running the Failing Test• Making the Test Pass• Repeating it Again for Other Tests In the List
• Breadth-First vs. Depth-First• Let’s Not Forget to Re-factor
• Both Production & Test Code Re-factoring• Remove Duplication• Re-factoring toward smaller methods• Keeping things in balance
Confidential 10
TDD Concepts & Patterns
• What to Test & in What Order? • Details vs. big picture• Uncertain vs. familiar• High value vs. low-hanging fruit• Happy path vs. error situations
• Implementation Strategies• Faking it• Triangulation• Obvious Implementation
• Prime Guidelines for Test-Driving• Do Not Skip Re-factoring• Get to Green Fast• Slow Down After a Mistake
Confidential 11
TDD Concepts & Patterns (Continued)
• Unit-Testing Concepts • Using Test Fixtures• Using Test Doubles (More Details to Follow)
Confidential 12
Test Doubles (Alternative Implementations)
• An Example of a Test Double • Reasons for Having Test Doubles
• Avoiding a Landslide of Failing Tests• Avoiding Slow Running Tests• Avoiding Availability (Existence) Problems• Avoiding Difficult Instantiation And/Or Configuration
• Test Double Taxonomy• Stubs• Fakes• Mocks
Exercise
Confidential 13
Point Of Sale System…
• As an example, we’ve chosen a point-of-sale system, like one might encounter in any retail store. A sales clerk passes items over a scanner that reads the barcode. A display monitor shows information on each item as it is scanned and also tracks the total for each sale of one or more items to a given shopper.
• Sample User Story• The total price of all items in the sale is available.
smart.proven.bankable.
Confidential
Guidelines for Testable Design
Confidential
Confidential 15
Guidelines for Testable Design
• Choose composition over inheritance• Avoid static and the Singleton• Isolate dependencies• Inject dependencies
Confidential 16
Choose Composition Over inheritance
• Inheriting lets subclass to inherit all the functionality of Superclass.
• Downside – Introduce inconvenient constraints related to Instantiating the subclass in test harness.
• Composition allows more flexible design solution for reusability.• Example – Allows to instantiate the composite object with
alternative implementations
Confidential 17
Dependency Injection
• Refers to supplying an external dependency to a software component.
• Forms of Dependency Injection –• interface injection, in which the exported module provides an
interface that its users must implement in order to get the dependencies at runtime • setter injection, in which the dependent module exposes a setter
method which the framework uses to inject the dependency .• constructor injection, in which the dependencies are provided
through the class constructor.
Confidential 18
Constructor Injection..
public class ImportantClass {
IFoo foo;
public ImportantClass()
{
this.foo = new EnterpriseFoo();
}
void doReallyImportantStuff() {
this.foo.bar();
}
}
public class ImportantClass {
IFoo foo;
public ImportantClass(IFoo foo) { this.foo = foo;
}
void doReallyImportantStuff() {
this.foo.bar();
}
}
smart.proven.bankable.
Confidential
Refactoring
Confidential
Confidential 20
What is Refactoring?
•Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior.
• Its heart is a series of small behavior preserving transformations.
Confidential 21
What is Refactoring?
•Each transformation (called a 'Refactoring') does little, but a sequence of transformations can produce a significant restructuring.
•The system is also kept fully working after each small Refactoring, reducing the chances that a system can get seriously broken during the restructuring.
Confidential 22
Why Do we refactor ?
• Improves the design of the software.
• Easier to maintain and understand.
• Easier to facilitate change.
• More flexibility.
• Increased reusability.
• To help find bugs
Confidential 23
When do we refactor ?
• Duplicated code.
• Long method.
• Long parameter list..
• Improper naming.
Confidential 24
Refactoring Techniques
• Here are some of the Refactoring techniques discussed in the session
– Sprout Method
– Sprout Class
– Wrap Method
– Wrap Class
Confidential 25
Sprout Method
• While adding new feature to the existing method it can formulated completely as a new method. How ?
– Write the code in the new method.– Call it from the places where the new functionality needs to be
• Example:Public class TransactionGate{Public void postEntries(List entries){ for(Iterator it=entries.iterator();it.hasNext();){
Entry entry=(Entry)it.next();entry.postDate();
}}transactionBundle.getListManager.add(entries) ;}}
Confidential 26
Sprout method - Continue
Public class TransactionGate
{
Public void postEntries(List entries){
List entriesToAdd=uniqueEntries(entries);
--------
}
}
transactionBundle.getListManager.add(entries) ;
}
List uniqueEntries(List entries) {
//Unique Entries impl comes here
}
}
Confidential 27
Sprout method - steps
• Identify the place to make the code change
• If the change can be formulated as a single sequence of statement write down a call for the new method that will do the work involved and comment it out.
• Determine the local variables you need from the source method and make them arguments for the call.
Confidential 28
Sprout method - steps
• Determine whether the sprouted method will need to return values to source method. If so change the call so that its return value is assigned to a variable.
• Develop the sprout method using TDD.
• Remove the comment in the source code to enable the call.
Confidential 29
Sprout Class
• Sprout method is powerful but in some tangled dependency situation it is not powerful though
• Example: std::string QuaterlyReportGenerator::generate(){ std::veactor<Result>
results=database.queryResults(beginDate,endDate) ; std::string pageText ; pageText+=“<html><head><title>Quaterly Report</title></head> <body><table>” if(results.size()!=0){ //html page generator code comes here}}
Confidential 30
Sprout class - Continues
• Suppose if we need to add a new functionality.– Add a Header row for the HTML table its producing.
– “QuaterlyReportGenerator” is a huge class to get into test harness.
• Formulate the new change into a little class and bring it in test harness.
class QuaterlyReportTableHeaderGenerator{
public:
string generate() ;
}
Confidential 31
Sprout class - continues
• Move the generate method to a interface.
Class HTMLGenerator{
public :
virtual ~HTMLGenerator() ;
virtual string generate() ;
}• Make QuaterlyReportGenerator and
QuaterlyReportTableHeaderGenerator inherit from HTMLGenerator.
• Now the new class is in testharness at somepoint in time we would be able to get the QuaterlyReportGenerator also in test harness.
Confidential 32
Sprout class - steps
• Identify where you need to make the code change.
•Think of a good name for the class, create an object of the class and call the method that will do the work ; then comment the lines.
•Determine the local variables you need from the source method and make them as constructor arguments.
Confidential 33
Sprout class - steps
•Determine whether the sprouted class will need to return value to the source method.
• If so, provide a method in the class that will supply those values and add a call to the source method to receive the same.
•Develop the sprout class test first.
Confidential 34
Further..
• Recommended reading– Working Effective with Legacy code By Michael Feathers
– http://www.refactoring.com/sources.html– Refactoring: Improving the Design of Existing Code – Martin Fowler
– http://tech.groups.yahoo.com/group/refactoring/
– http://www.refactoring.com/tools.html