TDD And Refactoring

34
smart.proven.bankable. Confidenti al TDD and Refactoring Ganesan R Sr.Software Developer Sabre Holdings

description

Ganesan Rajamani's presentation on Test Driven Development and Refactoring. Was presented at the Agile Goa 2008 conference.

Transcript of TDD And Refactoring

Page 1: TDD And Refactoring

smart.proven.bankable.

Confidential

TDD and RefactoringGanesan RSr.Software DeveloperSabre Holdings

Page 2: TDD And Refactoring

Confidential 2

Game Plan

• Introduction (A Little Bit of Theory)• TDD Basics• Guidelines for Testable Design• Refactoring and its Techniques

Page 3: TDD And Refactoring

smart.proven.bankable.

Confidential

Introduction

Confidential

Page 4: TDD And Refactoring

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

Page 5: TDD And Refactoring

Confidential 5

Testing Terminology

Production Code

Unit Tests

Integration Tests

FixturesAcceptance Tests

ATDD TDD

Programmers’ TestsCustomer’s Tests

Page 6: TDD And Refactoring

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

Page 7: TDD And Refactoring

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

Page 8: TDD And Refactoring

smart.proven.bankable.

Confidential

TDD Basics

Confidential

Page 9: TDD And Refactoring

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

Page 10: TDD And Refactoring

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

Page 11: TDD And Refactoring

Confidential 11

TDD Concepts & Patterns (Continued)

• Unit-Testing Concepts • Using Test Fixtures• Using Test Doubles (More Details to Follow)

Page 12: TDD And Refactoring

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

Page 13: TDD And Refactoring

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.

Page 14: TDD And Refactoring

smart.proven.bankable.

Confidential

Guidelines for Testable Design

Confidential

Page 15: TDD And Refactoring

Confidential 15

Guidelines for Testable Design

• Choose composition over inheritance• Avoid static and the Singleton• Isolate dependencies• Inject dependencies

Page 16: TDD And Refactoring

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

Page 17: TDD And Refactoring

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.

Page 18: TDD And Refactoring

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();

}

}

Page 19: TDD And Refactoring

smart.proven.bankable.

Confidential

Refactoring

Confidential

Page 20: TDD And Refactoring

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.

Page 21: TDD And Refactoring

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.

Page 22: TDD And Refactoring

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

Page 23: TDD And Refactoring

Confidential 23

When do we refactor ?

• Duplicated code.

• Long method.

• Long parameter list..

• Improper naming.

Page 24: TDD And Refactoring

Confidential 24

Refactoring Techniques

• Here are some of the Refactoring techniques discussed in the session

– Sprout Method

– Sprout Class

– Wrap Method

– Wrap Class

Page 25: TDD And Refactoring

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) ;}}

Page 26: TDD And Refactoring

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

}

}

Page 27: TDD And Refactoring

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.

Page 28: TDD And Refactoring

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.

Page 29: TDD And Refactoring

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}}

Page 30: TDD And Refactoring

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() ;

}

Page 31: TDD And Refactoring

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.

Page 32: TDD And Refactoring

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.

Page 33: TDD And Refactoring

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.

Page 34: TDD And Refactoring

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