You are on page 1of 144

DESIGN PRINCIPLES AND PATTERNS

05. CLEAN CODE


Nguyen Thi Thu Trang
trangntt@soict.hust.edu.vn
2

Content
1. Introduction Clean Code
Keep it Clean, Your Mother Doesn’t Work Here!

2. Meaningful Names
3. Clean Function/Method
4. Clean Class
5. Clean Test
3

1.1. What is Clean Code?


• Code must be easily readable
• Code must be easily understandable
• Code should be easily modified

… by anyone

Clean Code = Good Code


4

Why Clean Code?


Read code
• Programming is not what to 10%

tell the computer what to do


• Programming is what to tell to
another human what we want
the computer to do
• Unfortunately sometimes
the "other man" is ourselves
Write code
• Eventually we become authors 90%
Time programmers do at work
5

Why Clean Code? (2)


• There’s no such thing
as write-once code
• Bug Fixes
• Business Changes
• Enhancements
• New Functionality
6

What Prevents Clean Code?


• Number one reason:

“I’ll clean it up later”

• Pro Tip: “Later” never comes!


7

Going fast
”The only way to go fast, is to go well”
Robert C. Martin
• Don’t hack and skip testing just to finish at
the end of an iteration
• Write unit tests
• Fix the code
• Refactor
8

What is Bad Code (Code Smell)?


• Hard to read and understand
• Spaghetti code (things get wild within the component)
• Misleading

• Poor reusability
• It breaks when you change it
• Changes to component require inspecting/ modifying
more code

•è Low cohesion and Tight coupling


9

Review: Low Cohesion and Tight coupling


• Spaghetti code (things get wild within the
component)
• Poor reusability
• Changes to component require inspecting/
modifying more code
• Performance suffers
10

1.2. Code Smells and Refactoring


• Code smells (Bad smells):
Certain structures in the code
that suggest the possibility of
refactoring
• Refactoring means “to improve
the design and quality of
existing source code without
changing its external behavior”
Martin Fowler
• Unit tests guarantee that
refactoring preserves the behavior
Refactoring: The Typical Process
1. Save the code you start with
• Check-in or backup the current code
2. Prepare tests to assure the behavior after the
code is refactored
• Unit tests / characterization tests
3. Do refactoring one at a time
• Keep refactoring small
• Don't underestimate small changes
4. Run the tests and they should pass / else revert
5. Check-in (into the source control system)
1
1
Refactoring Tips
• Keep refactoring small
• One at a time
• Make a checklist
• Make a "later" / TODO list
• Check-in / commit frequently
• Add tests cases
• Review the results
• Pair programming
• Use tools/IDEs
• Visual Studio + add-ins / Eclipse + plugins / others 1
2
13

Java Refactorings in Eclipse


• Renaming resources
• Field, method, class, inner and anonymous class
• Moving to another package
• Class hierarchy modifications
• Make an interface/superclass from a class
• Code level mods
• Make a code segment a function
14

1.3. Main Principles for Clean Code


• Keep it simple: KISS principle
• Avoid duplication: DRY principle
• Avoid redundancy: YAGNI principle
• Boy scout rule
• Leave your code better than you found it
• Less coupling and more cohesion
• SOLID Principles (later)
• Law of Demeter
• Design Patterns (later)
15

D.R.Y Principle
• Don’t Repeat Yourself
• Applicable whenever we copy / paste a
piece of code
• Just use a method or a class
16

K.I.S.S Principle
• Keep It Simple and Stupid
• Whenever we want to implement a method to do
all things
17

Y.A.G.N.I principle
• You Ain’t Gonna Need It
• Don’t write methods which are not yet
necessary (may be you will never need
them)
• Unused classes, methods, parameters,
variables à remove them
• Somehow related with KISS
18

Law of Demeter
Karl Lieberherr i and colleagues
• Law of Demeter: An object should know as little as possible
about the internal structure of other objects with which it
interacts – a question of coupling
• Or… “only talk to your immediate friends”
• Closely related to representation exposure and
(im)mutability
• Bad example – too-tight chain of coupling between classes
general.getColonel().getMajor(m).getCaptain(cap)
.getSergeant(ser).getPrivate(name).digFoxHole();

• Better example
general.superviseFoxHole(m, cap, ser, name);
19

An object should only send messages to …


(More Demeter)

• itself (this) Guidelines: not strict rules!


But thinking about them
• its instance variables will generally help you
• its method's parameters produce better designs
• any object it creates
• any object returned by a call to one of this's methods
• any objects in a collection of the above

• notably absent: objects returned by messages sent to


other objects
20

S.O.L.I.D Principles
• SRP: The Single Responsibility Principle
• OCP: The Open Closed Principle
• LSP: The Liskov Substitution Principle
• ISP: The Interface Segregation Principle
• DIP: The Dependency Inversion Principle
21

Content
1. Introduction Clean Code
Keep it Clean, Your Mother Doesn’t Work Here!

2. Meaningful Names
3. Clean Function/Method
4. Clean Class
5. Clean Test
22

2.1. Use Intention-revealing Names


• A name of a variable, function/method, class should
reveal the intent
• Why it exists
• What it does
• How it is used

int d; // elapsed time in days

A name requires a comment è Does not reveal intent


23

Example
What is the purpose of this code?

• What kinds of things are in


theList?
• What is the significance of the zeroth
subscript of an item in theList?
• What is the significance of the value
4?
• How would we use the returned list?
24

Solution
• The simplicity of this code is not changed, but much more
meaningful

The power of choosing good names!


25

2.2. Avoid Disinformation


• Avoid false clues that obscure the meaning of
code
• For example: use accountList as variable name
but this variable is not actually a list
• Avoid giving similar names for different
variables:
• XYZControllerForEfficientHandlingOfStrings
• XYZControllerForEfficientStorageOfStrings
26

2.3. Make meaningful distinctions

What is the difference between two names: a1 and a2?


27

Avoid noise words


• 2 classes: ProductInfo & ProductData
• è Info and Data are noise words because they do not
reveal anything different
• 2 variables: zork & theZork
• How could we decide which variable should be used?

If names must be different, then they should


also mean something different!
28

2.4. Using pronounceable names

VS
29

2.5. Using searchable names


• Should give searchable names for a variable or constant
that are used in multiple places of code

Single letter and numeric constants are not easy to locate

VS
30

2.6. Class names


• Name of classes or objects should be noun or noun
phrase
• A class name should not be a VERB!
• Customer, Account, Address etc.
• Class name should also begin with a capitalized letter
31

2.7. Method names


• Methods should have verb or verb phrase names
• Accessors, mutators and predicates should be named
with prefix: get, set, is
• Should not capitalize the first letter of method name
• When constructors are overloaded, use static factory
methods with names that describe arguments
32

2.8. Add meaningful context


firstName
lastName
street
houseNumber form an address
city
state
zipcode
è They should belong to the class Address?
33

Practice: Clean code with meaningful


names
• Checking if the codebase are clean with
meaningful names
• If not, re-design and re-factor
34

Content
1. Introduction Clean Code
Keep it Clean, Your Mother Doesn’t Work Here!

2. Meaningful Names
3. Clean Function/Method
4. Clean Class
5. Clean Test
35

3.1. Do ONE Thing


• Functions should do one thing => Cohensive
They should do it well
They should do it only

• How to know what “one thing” is?


• A function is doing more than “one thing” if you
can extract another function from it with a name
that is not merely a restatement of its
implementation
• ONE Thing: ONE Level of Abstraction
36

ONE Level of Abstraction


• A function with these three steps does one,
two or three things?
• Determining whether the page is a test page
• If so, including setups and teardowns
• Rendering the page in HTML
public static String testableHtml(PageData pageData, 37
boolean includeSuiteSetup) throws Exception{
WikiPage wikiPage = pageData.getWikiPage(); //very high level
StringBuffer buffer = new StringBuffer();
if (pageData.hasAttribute("Test")){
if (includeSuiteSetup){ ... }
...
}
buffer.append(pageData.getContent());
HtmlUtil.java (v1)
if (pageData.hasAttribute("Test")){
WikiPage teardown = ...
...
buffer.append("\n"); //low level
... Multiple levels of
}
if (includeSuiteSetup) { abstraction
WikiPage suiteTeardown = ...
...
String pagePathName = PathParser.render(pagePath); //intermediate
...
}
pageData.setContent(buffer.toString());
return pageData.getHtml(); //very high level
}
38

HtmlUtil.java (v2)
public static String renderPageWithSetupsAndTeardowns(
PageData pageData, boolean isSuite) throws Exception{
boolean isTestPage = pageData.hasAttribute("Test"); //low
if (isTestPage){
WikiPage testPage = pageData.getWikiPage(); // high
StringBuffer newPageContent = new StringBuffer();
includeSetupPages(testPage, newPageContent, isSuite);
newPageContent.append(pageData.getContent();
includeTeardownPages(testPage, newPageContent, isSuite);
pageData.setContent(newPageContent.toString());
}
return pageData.getHtml(); Two levels of
}
abstraction
39

HtmlUtil.java (v3)
public static String renderPageWithSetupsAndTeardowns(
PageData pageData, boolean isSuite) throws Exception{
if (isTestPage(pageData))
includeSetupAndTeardownPages(pageData, isSuite);
return pageData.getHtml();
}

ONE level of abstraction


• You can’t extract another function from it
with a name that is not merely a
restatement of its implementation
Clean for Cohensive Functions/Methods
• Large repeating code fragments à extract
duplicated code in separate method
• Large methods à split them logically
• Large loop body or deep nesting à extract
method
• Method has weak cohesion à split into several
methods

4
0
41

3.2. Loosely Coupled Functions/Methods


• Avoid Flag arguments (Control coupling)
• Passing a boolean into a function is a truly terrible
practice: Function does one thing if the flag is true and
another if the flag is false
if (delPage(page) == E_OK) { 42
if (registry.deleteRef(page.name) == E_OK) {
if(configKeys.deleteKey(page.name.makeKey())
== E_OK){
logger.log("page deleted");
} else {
logger.log("configKey not deleted");
}
} else {
logger.log("deleteReference from registry failed"); }
}else {
logger.log("delete failed");
return E_ERROR;
}
try {
deletePage(page);
registry.deleteRef(page.name);
configKeys.deleteKey(page.name.makeKey());
} catch (Exception e) {
logger.log(e.getMessage());
}
43

3.2. (cont.)
• Extract Try/Catch Blocks
• Try/Catch mixes error processing with normal processing
Error Processing
• Extract bodies of try/catch out into separated functions

Nice Separation of functions!!!

the processes of fully deleting a page


44

3.2. (cont.)
• Prefer Exceptions to Returning Error Codes
(Control Coupling)

This method returns a special


value that indicates an error?
45

3.2. (cont.)
• Prefer Exceptions to Returning Error Codes
(Control Coupling) – Benefits:
1. No more code for checking various error codes
2. Exception classes can implement their own method,
i.e., error handling functionality
3. Error codes can’t be used in a constructor, since a
constructor must return only a new object
46

3.2. (cont.)
• Have No Side Effect
• Not do hidden things
• Side Effect: Make unexpected changes to the variables
of its own class or parameters or global data (Common
coupling)
47

This function matches a username and a password.


Does it contain any side effects?
48

Temporal coupling: what if checkPassword() is called out


of order checkout?
49

Clean for Loosely Coupled Functions


• Public non-constant fields à make them private
and define necessary accessing properties

Why we should refactor public field?


50

Clean for Loosely Coupled Functions


A method calls more methods from another class
than from its own class à Move it

WHY?
51

Clean for Loosely Coupled Functions


A method calls more methods from another class
than from its own class à Move it

• Classes are more internally coherent


• Reduce dependency between classes
52

Clean for Loosely Coupled Functions


A method has too many parameters
53

Clean for Loosely Coupled Functions


Problems:
• A high-couling in function because each
parameter has its own goal
Solutions:
1) Replace Parameter with Method Call
2) Introduce Parameter Object
54

Replace Parameter with Method Call


• If some of the arguments are results of method calls of
another object

int basePrice = quantity * itemPrice;


double seasonDiscount = this.getSeasonalDiscount();
double fees = this.getFees();
double finalPrice = discountedPrice(basePrice, seasonDiscount,
fees);

int basePrice = quantity * itemPrice;


double finalPrice = discountedPrice(basePrice);
55

Introduce Parameter Object


• If there are several unrelated data elements, it is better to
merge them into parameter object
56

3.3. Method Refactoring


• Data-Level Refactoring
• Statement-Level Refactoring
• Method-Level Refactoring
Data-Level Refactoring
• Rename a variable with more informative name
• Replace a magic number with a named constant

• Reduce duplicate use of a number or string in the code


• Easier to change the value of a constant than to search for this number 5
throughout the entire codebase 7
Data-Level Refactoring
Replace an expression with a method
• To simplify it or avoid code duplication

5
8
Data-Level Refactoring
Move an expression inline

• Improve the readability of the program by getting rid of the unnecessary variable

5
9
Data-Level Refactoring
Introduce an intermediate variable
• Introduce explaining variable

More readable code!!!

Drawbacks?
6
0
Data-Level Refactoring
Convert a multi-use variable to a multiple single-use
variables
• Create separate variable for each usage

Each component does one thing, no more than one thing!


6
1
Data-Level Refactoring
Convert a data primitive to a class
• Improve realtedness inside classes
• Data and relevant behaviors are inside a single class

What if the field customer has its own


behavior and associated data?

6
2
Data-Level Refactoring
Convert a set of type codes (constants) to enum

What is type code? A set of numbers or strings that form a list of


allowable values for some entity.

6
3
Data-Level Refactoring
Convert a set of type codes (constants) to enum/class

Problem: A class has a field that contains type code

What if someone sends unintended or wrong values


to the filed bloodgroup?
6
4
Data-Level Refactoring
Convert a set of type codes to a class with subclasses with
different behavior
Problem Context: You have a method that accepts one of
the type values in the parameters (i.e., user),
USER_TYPE_ADMIN = “ADMIN”
if (user.getType) === UserType.USER_TYPE_ADMIN
doSomething()

What if the method receives the same string but in lower case “admin”?

6
5
Data-Level Refactoring
Convert a set of type codes to a class with subclasses with
different behavior

Benefits:
• Delete control flow code by moving the code to appropriate subclasses
• Need to add a new value for a coded type è Add new subclass, so OCP!
6
6
Data-Level Refactoring
What if:
• your type code affects the behavior of a class
• the value of coded type can be changed during the
object’s lifetime?
You cannot use:
• Replace Type Code with Class/Enum
• Replace Type Code with Subclasses

Solution: Strategy/State Pattern

6
7
Replace type code by Strategy/State

If a new value of a coded type is needed, just create a new state subclass!

6
8
Data-Level Refactoring
Change an array to an object
• When you use an array with different goals for each element

What if we put something in the wrong element?

Solution: Replace the array with an object that will have separate
fields for each element.

6
9
Data-Level Refactoring
Encapsulate a collection
Problem: A class contains a collection field and a simple getter/setter for working
with this collection

• Client can change the collection content


without the knowledge of the owner class
• Should not assign a value to a collection

7
0
71

Statement-Level Refactoring
• Decompose a boolean expression
• Move a complex boolean expression into a well-
named boolean function
72

Statement-Level Refactoring
• Consolidate Conditional Expression
• Multiple conditionals that lead to the same
result or action
73

Statement-Level Refactoring
• Consolidate duplicated code in conditionals
74

Statement-Level Refactoring
Return as soon as you know
the answer instead of
assigning a return value
75

Statement-Level Refactoring
Replace conditionals with polymorphism
Problem: We have a conditional that performs various actions depending on
object type or properties
76

Tell don’t Ask:


• Dont ASK an object about its state
and then performing actions based
on this
• Just TELL the object what it needs
to do and let it decide for itself
HOW to do that
77

Statement-Level Refactoring
Use null-object design pattern instead of
checking for null

Need ALWAYS to check for null in your


code to avoid Null Pointer Exception
78

Method-Level Refactoring
• Extract method / inline method
• Rename a method
• Convert a long routine to a class
• Add / remove parameter
• Combine similar methods by parameterizing them
• Substitute a complex algorithm with simpler
• Separate methods whose behavior depends on
parameters passed in (create new ones)
• Pass a whole object rather than specific fields
• Encapsulate downcast / return interface types
79

Practice: Clean Method


• Checking if all methods in codebase are
clean
• If not, re-design and re-factor
80

Content
1. Introduction Clean Code
Keep it Clean, Your Mother Doesn’t Work Here!

2. Meaningful Names
3. Clean Function/Method
4. Clean Class
5. Clean Test
81

Signs of Code Smells in Class


• Large class
• Classes usually start small, but get bloated as
the program grows
• Often does more than one task è more than
one reason to change it
82

Signs of Code Smells in Class (2)


• Refused bequest
• Subclass uses only some of the methods and
properties inherited from its parents
83

Signs of Code Smells in Class (3)


• Alternative Classes
with Different
Interfaces
• 2 classes perform
identical functions
but have different
method names
84

Signs of Code Smells in Class (3)


• Divergent change
• Make changes to a class requires change many
unrelated methods
• For example, when adding a new product type you have to change
the methods for finding, displaying, and ordering products.
85

Signs of Code Smells in Class (4)


• Shotgun Surgery
• A single change is made to multiple classes
simulteneously
86

Signs of Code Smells in Class (5)


• Parallel Inheritance Hierarchies
• Whenever creating a subclass for a class, it
requires to create a subclass for another class
87

Signs of Code Smells in Class (6)


• Inappropriate Intimacy
• One class uses the internal fields and methods of
another class
• Good designed classes should know as little as
possible about each other
88

Signs of Code Smells in Class (7)


• Incomplete library class
• The library does not provide a feature you need
and it is impossible to modify the library to
implement it
89

Practice: Checking code smells in


codebase
• Checking if there are signs of code smell in
all classes of the codebase
90

4.3. Class Refactoring


• Class-Level Refactoring
• Interface-Level Refactoring
• System-Level Refactoring
91

4.3. Class Refactoring


• Class-Level Refactoring
• Interface-Level Refactoring
• System-Level Refactoring
Restructure class
• Pull up field / method
• When there is a duplication of field, method in subclasses è
move them up to superclass

9
2
Restructure class
• Pull up constructor body
• Your subclasses have constructors with code that’s mostly
identical

9
3
Restructure class
• Push down field/method
• If some members of superclass are used only in a few
subclasses

9
4
Extract subclass
• Code smell: a class has features that are used only in
certain cases
• Solution: create a subclass and use it in these cases
• Case study:
• Car class, you had field isElectricCar and depending on
it, in the refuel() method the car is either fueled up with gas
or charged with electricity.

9
5
96

Extract superclass
• Code smell: two
classes with common
fields and methods
(duplication code)
• Solution: create a
superclass containing
all duplication code
97

Extract superclass
• Code smell: two
classes with common
fields and methods
(duplication code)
• Solution: create a
superclass containing
all duplication code
98

Collapse Hierarchy
• Code smell: Subclass is practically the same as its
superclass
• Solution: Merge the subclass and superclass
99

Form Template Method


• Problem: Your subclasses implement algorithms that
contain similar steps in the same order.
• Solution: Move the algorithm structure and identical
steps to a superclass, and leave implementation of the
different steps in the subclass
100

Form Template Method (2)


• Case study:
101

Form Template Method (3)


• Solution:
Replace Inheritance with Delegation
• Problem: A subclass that uses only a portion of the
methods of its superclass (or it is impossible to inherit
superclass data)
• Why refactor?
• The subclass violates the LSP

• Solution:
• Create a field and put a superclass object in it, delegate
methods to the superclass object, and get rid of inheritance

1
0
Replace Inheritance with Delegation (2)
• Case study:

1
0
Replace Inheritance with Delegation (3)
• Case study:

1
0
Replace Inheritance with Delegation (4)
• Case study: A navigation application for casual
travelers

1
0
Replace Inheritance with Delegation (4)
• Solution: Inheritance. Is it good enough?

1
0
Replace Inheritance with Delegation (5)
• Solution: Delegation

Strategy Pattern
1
0
Replace Delegation with Inheritance
• Problem: A class contains many simple methods that
delegate to all methods of another class

1
0
Replace Delegation with Inheritance
• Solution: Make the class a delegate inheritor, which makes the
delegating methods unnecessary
• When not to use?

1
0
110

4.3. Class Refactoring


• Class-Level Refactoring
• Interface-Level Refactoring
• System-Level Refactoring
Extract interfaces
• Problem: Multiple clients are using the same
part of a class interface or part of the interface
in two classes is the same
• Solution: move this identical portion to its own
interface
• Interfaces are very apopos when classes play
special roles in different situations

1
1
Extract interfaces (2)
• Case study

1
1
Hide delegate
• Problem: The client gets object B from a field or
method of object A, then the client calls a
method of object B
• Code smell: message chain
• a.b().c().d()

• Why refactoring?
• a: server – the object to which the client has direct access
• c(): return an object – delegate – the end object that
contains the functionality needed by the client
• These sequences of calls involve the client in navigation
along the class structure, any changes in these 1
interrelationships will require changes on the client side 1
Hide delegate (2)
• Solution: Create a new method in class A that delegates
the call to object B

manager = aPerson.getDepartment().getManager() 1
1
Hide delegate (3)
• Solution: Create a new method in class A that delegates
the call to object B
• Benefit?
manager = aPerson.getManager()

return this.department.manager

1
1
Remove Middle Man
• Problem: A class has too many methods that
simply delegate to objects of other classes
• Result from the elimination of Message Chains
• Solution: Delete these methods and force the
client to call the end methods directly

1
1
Introduce Foreign Method
• Problem: A utility class doesn’t contain the method
that you need and you can’t add the method to the
class

1
1
Introduce Foreign Method (2)
• Solution: Add the method to a client class and
pass an object of the utility class to it as an
argument

What if you want to reuse this method in other classes? 1


1
Introduce Local Extension
• Problem: A utility class doesn’t contain some
methods that you need and it is impossible to add
these methods to this class

You want to reuse this method in


other classes
1
1
Introduce Local Extension (2)
• Solution: Create a new class
containing the methods and
make it either the child or
wrapper of the utility class
• Create subclass: inherit from
the utility class
• Create wrapper class: when
the utility class prevent
inheritance (with final)
• Apply Decorator pattern
1
2
Using Decorator to resolve the Incomplete
Library Class Code Smell
• Decorator is also known as – a structural pattern
• Intent: Attach new behaviors to objects by placing these
objects inside special wrapper objects or via inheritance
• Case stydy:
• In your application, you use a notification library which lets other
programs notify their users about important events
• The base library allows you to send notification email

1
2
Using Decorator to resolve the Incomplete
Library Class Code Smell (2)
• Case stydy:
• Problem: You need other kinds of notification than just email
notification
• You just inherit the base class Notifier

1
2
Using Decorator to resolve the Incomplete
Library Class Code Smell (3)
• Case stydy:
• What if someone asks you to provide notification on multiple
channels?

1
2
Using Decorator to resolve the Incomplete
Library Class Code Smell (4)
• Case stydy:
• What if someone asks you to provide notification on multiple
channels?

! ! !
ps
Oo

1
2
Using Decorator to resolve the Incomplete
Library Class Code Smell (5)
• Case study – Solution: Using Aggregation or Composition
instead Inheritance

1
2
Using Decorator to resolve the Incomplete
Library Class Code Smell (6)
• Case study – Solution: We
leave the simple email
notification inside the base class
Notifier and turn all other
notification methods into
decorators
wrappee = notifier

1
2
Using Decorator to resolve the Incomplete
Library Class Code Smell (7)
• How does the client work?
• Client wraps a basic notifier
object into a set of decorators
that match the client’s
preferences
• The resulting object will be
structured as a stack

1
2
128

4.3. Class Refactoring


• Class-Level Refactoring
• Interface-Level Refactoring
• System-Level Refactoring
System-Level Refactoring
• Move class (set of classes) to another
namespace / assembly
• Provide a factory method instead of a simple
constructor / use fluent API
• Replace error codes with exceptions
• Extract strings to resource files
• Use dependency injection
• Apply architecture patterns

1
2
130

Practice: Clean Class


• Checking if all classes in codebase are
clean
• If not, re-design and re-factor
131

Content
1. Introduction Clean Code
Keep it Clean, Your Mother Doesn’t Work Here!

2. Meaningful Names
3. Clean Function/Method
4. Clean Class
5. Clean Test
132

5.1. Unit Tests


• Instant feedback
• Allows you to make changes to
code quickly
• Help you understand the design
of the code you are working on
• Writing testable code helps to
achieve better code quality
• Unit tests are a form of sample
code
• Test-first forces you to plan
before you code
133

Bug handling
1. Write a test that proves the existense of the bug
2. Fix the code and watch the test pass
à Automated regression testing

• Security benefit
• If we find a bug somewhere, we can make sure it does
not reappear
134

Testing, clean code and security


• Well-tested code gives us assurance and
confidence in our code base
• Well-tested code is easy to change
• We have a safety net
• Changeable code allows us to refactor
• Clean code
• Change design – improve the architecture
• Improve the readability
135

Testing, clean code and security (2)


• Clean readable code is easier to understand
”Comments are a failure to express oneself in code”
Robert C. Martin (paraphrased)
• Understandable code is easier to secure
• Security tests give us assurance and confidence
in our security controls
• Regression testing
• OWASP ESAPI
136

The Three Laws of TDD


• First Law: You may not write production code
until you have written a failing unit test.
• Second Law: You may not write more of a unit
test than is sufficient to fail, and not compiling is
failing.
• Third Law: You may not write more production
code than is sufficient to pass the currently failing
test
137

5.2. Clean Test


• Even more important in unit tests than it is
in production code
• How?
• Readability,
• Readability and
• Readability
• How?
• Clarity,
• Simplicity
• Density of expression
138

SerializedPageResponderTest.java
public void testGetDataAsHtml() throws Exception {
crawler.addPage(root, PathParser.parse("TestPageOne"), "test page");
request.setResource("TestPageOne");
request.addInput("type", "data");
Responder responder = new SerializedPageResponder();
SimpleResponse response =
(SimpleResponse) responder.makeResponse(
new FitNesseContext(root), request);
String xml = response.getContent();
assertEquals("text/xml", response.getContentType());
assertSubString("test page", xml); assertSubString("<Test", xml);
}

Clean Test: Readability


SerializedPageResponderTest.java (refactored)
public void testGetDataAsXml() throws Exception {
makePageWithContent("TestPageOne", "test page");
submitRequest("TestPageOne", "type:data");
assertResponseIsXML();
assertResponseContains("test page", "<Test");
}
139

5.3. One Assert per Test


• Easy to understand
• A single conclusion that is quick
EnvironmentControllerTest.java
@Test
public void turnOnCoolerAndBlowerIfTooHot() throws
Exception{
tooHot();
assertEquals("hBChl", hw.getState());
}
@Test
public void turnOnHeaterAndBlowerIfTooCold() throws
Exception{
tooCold();
assertEquals("HBchl", hw.getState());
}
140

One Assert per Test: Break into separate tests


SerializedPageResponderTest.java (refactored)
public void testGetDataAsXml() throws Exception {
makePageWithContent("TestPageOne", "test page");
submitRequest("TestPageOne", "type:data");
assertResponseIsXML();
assertResponseContains("test page", "<Test");
}
SerializedPageResponderTest.java (Single Assert)
public void testGetPageHierarchyAsXml() throws Exception{
givenPages("PageOne", "PageOne.ChildOne", "PageTwo");
whenRequestIsIssued("root", "type:pages");
thenResponseShouldBeXML();
}
public void testGetPageHierarchyHasRightTags() throws Exception{
givenPages("PageOne", "PageOne.ChildOne", "PageTwo");
whenRequestIsIssued("root", "type:pages");
thenResponseShouldContain(
"<name>PageOne</name>",
"<name>PageTwo</name>","<name>ChildOne</name>");
}
141

5.4. Single Concept per Test


• No more than one concept being tested
• Not long test functions that go testing one
miscellaneous thing after another
• May be multiple asserts for one concept

è Minimize the number of asserts per concept


and test just one concept per test function
142

Break into separate test functions


public void testAddMonths() {
SerialDate d1 = SerialDate.createInstance(31, 5, 2004);

SerialDate d2 = SerialDate.addMonths(1, d1);


assertEquals(30, d2.getDayOfMonth()); è Break
assertEquals(6, d2.getMonth());
assertEquals(2004, d2.getYYYY());
into
three
SerialDate d3 = SerialDate.addMonths(2, d1); test
assertEquals(31, d3.getDayOfMonth()); functions
assertEquals(7, d3.getMonth());
assertEquals(2004, d3.getYYYY());

SerialDate d4 = SerialDate.addMonths(1,
SerialDate.addMonths(1, d1));
assertEquals(30, d4.getDayOfMonth());
assertEquals(7, d4.getMonth());
assertEquals(2004, d4.getYYYY());
}
143

5.5. F.I.R.S.T
• Fast: Tests should be fast.
• To run them frequently
• Independent: Tests should not depend on each other
• One test should not set up the conditions for the next test
• Able to run each test independently and run the tests in any order
• Repeatable: Tests should be repeatable in any environment
• Able to run the tests in development, testing, production, QA
environment… without a network.
• Self-Validating: The tests should have a boolean output
either they pass or fail.
• Make the failure objective, not subjective (not manlually)
• Timely: The tests need to be written in a timely fashion
• Unit tests should be written just before the production code that makes
them pass
144

Practice: Clean Test


• Checking if all unit tests in codebase are
clean
• If not, re-design and re-factor

You might also like