refactoring legacy code driven by tests - eng

52
Refactoring legacy code driven by tests Luca Minudel + Saleem Siddiqui I’m the Refactoring Chicken I’m the TDD egg

Upload: luca-minudel

Post on 02-Jul-2015

446 views

Category:

Software


0 download

DESCRIPTION

re you working on code poorly designed or on legacy code that’s hard to test? And you cannot refactor it because there are no tests? During this Coding Dojo you’ll be assigned a coding challenge in Java, C#, Ruby, JavaScript or Python. You will face the challenge of improving the design and refactoring existing code in order to make it testable and to write unit tests. We will discuss SOLID principles, the relation between design and TDD, and how this applies to your solution. Reading list: Growing Object-Oriented Software, Guided by Tests; Steve Freeman, Nat Pryce Test Driven Development: By Example; Kent Beck Working Effectively with Legacy; Michael Feathers Agile Software Development, Principles, Patterns, and Practices; Robert C. Martin (C++, Java) Agile Principles, Patterns, and Practices in C#; Robert C. Martin (C#)

TRANSCRIPT

Page 1: Refactoring legacy code driven by tests - ENG

Refactoring legacy code driven by tests

Luca Minudel + Saleem Siddiqui

I’m the

Refactoring

Chicken

I’m the

TDD egg

Page 2: Refactoring legacy code driven by tests - ENG

Let’s clarify the scope of this Workshop

Page 3: Refactoring legacy code driven by tests - ENG

Languages supported in this Workshop

C#

Java

JavaScript

Ruby

Python

Page 4: Refactoring legacy code driven by tests - ENG

Automatic Testing Continuum

Specification

(documentation)

DesignVerification

Page 5: Refactoring legacy code driven by tests - ENG

Scope of this workshop

Specification

(documentation)

DesignVerification

Page 6: Refactoring legacy code driven by tests - ENG

Types of Automatic Tests

End-to-end, out-of-process, business facing

Unit, in-process, technology facing

Page 7: Refactoring legacy code driven by tests - ENG

Scope of this workshop

End-to-end, out-of-process, business facing

Unit, in-process, technology facing

Page 8: Refactoring legacy code driven by tests - ENG

Exercise 1: Tire Pressure Monitoring System

Alarm class:

monitors tire pressure and sets an alarm if the pressure falls

outside of the expected range.

Page 9: Refactoring legacy code driven by tests - ENG

Exercise 1: Tire Pressure Monitoring System

Alarm class:

monitors tire pressure and sets an alarm if the pressure falls

outside of the expected range.

Sensor class:

simulates the behavior of a real tire sensor, providing random

but realistic values.

Page 10: Refactoring legacy code driven by tests - ENG

Exercise 1: Tire Pressure Monitoring System

Write the unit tests for the Alarm class.

Refactor the code as much as you need to make the Alarm

class testable.

Page 11: Refactoring legacy code driven by tests - ENG

Exercise 1: Tire Pressure Monitoring System

Write the unit tests for the Alarm class.

Refactor the code as much as you need to make the Alarm

class testable.

Minimize changes to the public API as much as you can.

Page 12: Refactoring legacy code driven by tests - ENG

Exercise 1: Tire Pressure Monitoring System

Write the unit tests for the Alarm class.

Refactor the code as much as you need to make the Alarm

class testable.

Minimize changes to the public API as much as you can.

Extra credits:

Alarm class fails to follow one or more of the SOLID principles.

Write down the line number, the principle & the violation.

Page 13: Refactoring legacy code driven by tests - ENG

The SOLID acronym

S single responsibility principle

O open closed principle

L Liskov substitution principle

I interface segregation principle

D dependency inversion principle

Page 14: Refactoring legacy code driven by tests - ENG

Dependency Inversion Principle (DIP)

Martin Fowler's definition:

a) High level modules should not depend upon

low level modules, both should depend upon

abstractions.

b) Abstractions should not depend upon details,

details should depend upon abstractions.

Page 15: Refactoring legacy code driven by tests - ENG

Dependency Inversion Principle (DIP)

Both low level classes and high level classes

should depend on abstractions.

High level classes should not depend on low

level classes.

Page 16: Refactoring legacy code driven by tests - ENG

DIP Violation In Example Code

High Level Class

Low Level Class

Dependency

Page 17: Refactoring legacy code driven by tests - ENG

Open Closed Principle (OCP)

Bertrand Meyer's definition:

Software entities (classes, modules, functions,

etc.) should be open for extension, but closed for

modification.

Page 18: Refactoring legacy code driven by tests - ENG

Open Closed Principle (OCP)

Classes and methods should be

open for extensions

&

strategically closed for modification.

So that the behavior can be changed and

extended adding new code instead of changing

the class.

Page 19: Refactoring legacy code driven by tests - ENG

OCP Violation In Example Code

Want to use a new type of sensor?

Must modify code; cannot extend it

Page 20: Refactoring legacy code driven by tests - ENG

Reference: WELC

Parametrize Constructor

Extract Interface

Page 21: Refactoring legacy code driven by tests - ENG

Exercise 2: Unicode File To Htm Text Converter

UnicodeFileToHtmTextConverter class:

formats a plain text file for display in a browser.

Page 22: Refactoring legacy code driven by tests - ENG

Exercise 2: Unicode File To Htm Text Converter

Write the unit tests for the UnicodeFileToHtmTextConverter

class.

Refactor the code as much as you need to make the class

testable.

Page 23: Refactoring legacy code driven by tests - ENG

Exercise 2: Unicode File To Htm Text Converter

Write the unit tests for the UnicodeFileToHtmTextConverter

class.

Refactor the code as much as you need to make the class

testable.

Minimize changes to the public API as much as you can.

Page 24: Refactoring legacy code driven by tests - ENG

Exercise 2: Unicode File To Htm Text Converter

Write the unit tests for the UnicodeFileToHtmTextConverter

class.

Refactor the code as much as you need to make the class

testable.

Minimize changes to the public API as much as you can.

Extra credits:

UnicodeFileToHtmTextConverter class fails to follow one or

more of the SOLID principles. Write down the line number,

the principle & the violation.

Page 25: Refactoring legacy code driven by tests - ENG

Feathers’ rules of thumb. Extended !

A test is not a unit test when:

It talks to the database

It communicates across the network

It touches the file system or reads config info

It uses DateTime.now() or Random

It depends on non-deterministic behavior

It can't run at the same time as any of your other unit

tests

You have to do special things to your environment

(such as editing config files) to run it.

Page 26: Refactoring legacy code driven by tests - ENG

Mike Cohn's Test Pyramid. Explained !

UItests

Integration tests

Unit tests

Page 27: Refactoring legacy code driven by tests - ENG

Reference: WELC

Parametrize Constructor

Extract Interface

Skin and Wrap the API

Page 28: Refactoring legacy code driven by tests - ENG

Refactoring and TDD

Should we inject this dependency?

Page 29: Refactoring legacy code driven by tests - ENG

Behavior of TextReader

TextReader documentation from MSDN

Non-idempotent behavior

Page 30: Refactoring legacy code driven by tests - ENG

Dependency injection and

idempotent behavior

Page 31: Refactoring legacy code driven by tests - ENG

Refactoring and TDD

Page 32: Refactoring legacy code driven by tests - ENG

Exercise 3: Ticket Dispenser

TicketDispenser class:

manages a queuing system in a shop.

There may be more than one ticket dispenser but the same

ticket should not be issued to two different customers.

Page 33: Refactoring legacy code driven by tests - ENG

Exercise 3: Ticket Dispenser

TurnTicket class:

represent the ticket with the turn number.

TurnNumberSequence class:

returns the sequence of turn numbers.

Page 34: Refactoring legacy code driven by tests - ENG

Write the unit tests for the TicketDispenser class.

Refactor the code as much as you need to make the

TicketDispenser class testable.

Exercise 3: Ticket Dispenser

Page 35: Refactoring legacy code driven by tests - ENG

Write the unit tests for the TicketDispenser class.

Refactor the code as much as you need to make the

TicketDispenser class testable.

Minimize changes to the public API as much as you can.

Exercise 3: Ticket Dispenser

Page 36: Refactoring legacy code driven by tests - ENG

Write the unit tests for the TicketDispenser class.

Refactor the code as much as you need to make the

TicketDispenser class testable.

Minimize changes to the public API as much as you can.

Extra credits:

TicketDispenser class fails to follow one or more of the OO

and SOLID principles. Write down the line number, the

principle & the violation.

Exercise 3: Ticket Dispenser

Page 37: Refactoring legacy code driven by tests - ENG

Reference: WELC

Parametrize Constructor

Extract Interface

Skin and Wrap the API

Introduce Instance Delegator

Page 38: Refactoring legacy code driven by tests - ENG

Exercise 4: Telemetry System

TelemetryDiagnosticControl class:

establishes a connection to the telemetry server through the

TelemetryClient,

sends a diagnostic request and receives the response with

diagnostic info.

TelemetryClient class:

simulates the communication with the Telemetry Server, sends

requests and then receives and returns the responses

Page 39: Refactoring legacy code driven by tests - ENG

Write the unit tests for the TelemetryDiagnosticControl class.

Refactor the code as much as you need to make the class

testable.

Exercise 4: Telemetry System

Page 40: Refactoring legacy code driven by tests - ENG

Write the unit tests for the TelemetryDiagnosticControl class.

Refactor the code as much as you need to make the class

testable.

Minimize changes to the public API as much as you can.

Exercise 4: Telemetry System

Page 41: Refactoring legacy code driven by tests - ENG

Write the unit tests for the TelemetryDiagnosticControl class.

Refactor the code as much as you need to make the class

testable.

Minimize changes to the public API as much as you can.

Extra credits:

TelemetryClient class fails to follow one or more of the OO and

SOLID principles. Write down the line number, the principle &

the violation.

Exercise 4: Telemetry System

Page 42: Refactoring legacy code driven by tests - ENG

Single Responsibility Principle (SRP)

A class should have only one reason to change.

Page 43: Refactoring legacy code driven by tests - ENG

Single Responsibility Principle (SRP)

There should never be more than one reason for

a class to change.

A class should have one and only one

responsibility.

Page 44: Refactoring legacy code driven by tests - ENG

Interface Segregation Principle (IRP)

Clients should not be forced to depend upon

interfaces that they do not use.

Page 45: Refactoring legacy code driven by tests - ENG

Interface Segregation Principle (IRP)

Clients should not be forced to depend upon

interface members that they don't use.

Interfaces that serve only one scope should be

preferred over fat interfaces.

Page 46: Refactoring legacy code driven by tests - ENG

Reference: SRP

http://www.objectmentor.com/resources/articles/srp.pdf

Pag. 151/152

Page 47: Refactoring legacy code driven by tests - ENG

Synergy between testing and design

Michael Feathers:

writing tests is another way to look the code and locally understand it and reuse it,

and that is the same goal of good OO design.

This is the reason forthe deep synergy

between testability and good design.

Page 48: Refactoring legacy code driven by tests - ENG

More references

Page 49: Refactoring legacy code driven by tests - ENG

More references

Page 50: Refactoring legacy code driven by tests - ENG

More references

Page 51: Refactoring legacy code driven by tests - ENG

References

http://scratch.mit.edu/projects/13134082/

http://vimeo.com/15007792

http://martinfowler.com/bliki/TestPyramid.html

http://martinfowler.com/bliki/StranglerApplication.html

http://www.markhneedham.com/blog/2009/07/07/domain-

driven-design-anti-corruption-layer/

http://www.objectmentor.com/resources/articles/srp.pdf

http://www.objectmentor.com/resources/articles/ocp.pdf

http://www.objectmentor.com/resources/articles/lsp.pdf

http://www.objectmentor.com/resources/articles/isp.pdf

http://www.objectmentor.com/resources/articles/dip.pdf

Page 52: Refactoring legacy code driven by tests - ENG

References / Links / Slides

On TwitterOn Twitter :

@S2IL

@LUKADOTNET