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

TDD And Refactoring

Embed Size (px)

DESCRIPTION

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

Citation preview

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