0% found this document useful (0 votes)
11 views82 pages

TDD and Design Principles Workshop

This document provides an agenda for a two-day course on Test Driven Development (TDD). Day 1 will cover topics like TDD naming conventions, emergent design through TDD, the SOLID principles, and outside-in TDD. Day 2 will focus on interaction driven design, identifying services from business rules, and deriving architecture through impact mapping. The document emphasizes that TDD helps produce clean, well-structured code through a process of writing failing tests first and then implementing minimal code to pass the tests.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views82 pages

TDD and Design Principles Workshop

This document provides an agenda for a two-day course on Test Driven Development (TDD). Day 1 will cover topics like TDD naming conventions, emergent design through TDD, the SOLID principles, and outside-in TDD. Day 2 will focus on interaction driven design, identifying services from business rules, and deriving architecture through impact mapping. The document emphasizes that TDD helps produce clean, well-structured code through a process of writing failing tests first and then implementing minimal code to pass the tests.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Crafted Design

sandro@[Link]
@sandromancuso
@codurance [Link]
Introductions
Who are you?
Which languages do you use?
What’s your experience with TDD?
Why this course?
Forget what you know…
… but just for today

There is no silver bullet when it comes to software.


Agenda
Day 1
• TDD & Naming
• Emergent design via TDD
• SOLID & BAP
• Outside-In TDD & Design

Day 2
• Interaction Driven Design (IDD)
• IDD Modeling
• Layers, hexagons, features, and components
• Identifying services from business rules
• Deriving architecture through Impact Mapping
Before we start…

Make sure you can do the following:


- Start a new project
- Write and run a failing test
assertThat(true, is(false));

Also, make sure you have the following installed:


- Mocking framework
- Maven (optional)
- Git (optional but recommended)
[Link]
Specify what you want
your code to do.
(failing test)

Red

Refactor Green

Clean your code and test. Create just enough


Remove duplication. code to satisfy the
(make it better) desired behaviour.
(make it work)
TDD Styles (schools)
Naming convention
Express what the class
should be able to do.

public class MyClassShould {


Reads as a
@Test public void full sentence

do_something_interesting() {
Start with
a verb. }

Tests should clearly specify the behaviour of the class under test.
Avoid technical terms.
Test method structure
public class BankAccountShould{

@Test public void


have_balance_of_zero_when_created() {
BankAccount bankAccount = new BankAccount();

assertThat([Link](), is(0));
}

@Test public void


have_the_balance_increased_after_a_deposit() {
given BankAccount bankAccount = new BankAccount();

when [Link](10);

then assertThat([Link](), is(10));


}

}
Test creation order Step 1: Name the class

public class BankAccountShould{


Step 2: Name the method
@Test public void
have_balance_increased_after_a_deposit() { Step 5: Setup

given BankAccount bankAccount = new BankAccount();

when [Link](10); Step 4: Trigger the


code
then assertThat([Link](), is(10));

}
Step 3: Define what you are testing
}

Production code should be created from the tests.


Bad naming used on tests

BankAccount testee = new BankAccount();


BankAccount sut = new BankAccount();
BankAccount ba = new BankAccount();

test_deposit_works() {…}
test_deposit_works_correctly() {…}
test_deposit() {…}
check_balance_after_deposit() {…}
Bad naming used on tests

BankAccount testee = new BankAccount();


BankAccount sut = new BankAccount();
BankAccount ba = new BankAccount();

If it is a bank account,
call it “bankAccount”

test_deposit_works() {…}
test_deposit_works_correctly() {…}
test_deposit() {…}
check_balance_after_deposit() {…}
Test methods should
indicate the expected
behaviour

increase_the_balance_after_a_deposit()
Tip for professional TDDers: Split your screen

public class MyClassShould { public class MyClass {

@Test public void public void


do_something_useful() { some_behaviour() {
} }

} }
Emergent design via TDD
Mars Rover Kata

Develop an API that moves a rover around on a grid.

Rules:

• You are given the initial starting point ([Link]N) of a rover.


• 0,0 are X,Y co-ordinates on a grid of 10 by 10.
• 0,0 is at the bottom left corner and 9,9 is at the top right corner.
• N is the direction it is facing (i.e. N,S,E,W).
• L and R allow the rover to rotate left and right.
• M allows the rover to move one point in the current direction.
• The rover receives a char array of commands e.g. RMMLM and returns the
finishing point after the moves e.g. [Link]N
• The rover wraps around if it reaches the end of the grid.
• The grid may have obstacles. If a given sequence of commands encounters an
obstacle, the rover moves up to the last possible point and reports the
obstacle and the position where it stopped, e.g. O:2:2:N
Emergent design via TDD

Demo
SOLID Principles
SRP

DIP OCP
SOLID
Principles

ISP LSP
Single Responsibility Principle - SRP
How do we know a class or method has a single
responsibility?

public void process(Trade trade) {


// 52 lines of code to process a trade
}
Single Responsibility Principle - SRP

Ø Outside view vs. Inside view

public void process(Trade trade) {


// 15 lines to validate the trade
// 10 lines to enrich the trade
// 4 lines to notify another system the trade was received
// 8 lines to convert trade to xml
// 10 lines of logic related to the processing the trade
// 5 lines to send the trade message out
}
Open Closed Principle (OCP)
Software entities (classes, modules, functions, etc)
should be open for extension, but closed for modification

Origins:
• Object-Oriented Software Construction - Bertand Meyer – 1988
• Information Hiding – David Parnas – 1972
• Protected Variations – Alistair Cockburn – 1996
• The Open/Closed Principle – Uncle Bob – 1996
• First approaches used abstract classes and inheritance.

OCP is the moral centre of a system architecture - Uncle Bob


OCP compliant design

[Link]
Uncle Bob
Class Invariant
public class Rectangle { public class Square extends Rectangle {
private double height;
private double width; public Square(double size) {
super(size, size);
public Rectangle(double h, double w) { }
[Link] = h;
[Link] = w; // getters and setters inherited from Rectangle
}
}
// getters

public void setHeight(double height) {


[Link] = height; What will happen when we call the
} setters?
public void setWidth(double width) {
[Link] = width;
}
}
Class invariant – a fix but not a solution

public class Square extends Rectangle { • In isolation, Rectangle and Square are
…. consistently valid;
public void setHeight(double height) {
[Link](height); • A sub-class is not a sub-class when:
setWidth(height); • Weaken the superclass behaviour in
} order to keep its own invariant

public void setWidth(double width) { • State becomes invalid when used in


[Link](width); the context of a super class.
setHeight(width); • “Is a” relationship is not always
} enough
}

Should Square extend Rectangle?


Liskov Substitution Principle - LSP
Behavioral subtyping

“Functions that use pointers or references to base classes must be


able to use objects of derived classes without knowing it. “

LSP violation
public void drawShape(Shape s) {
if (s instanceof Square) {
drawSquare((Square) s);
} else if (s instanceof Circle){
drawCircle((Circle) s);
}
}
Liskov Substitution Principle - LSP

A more subtle violation of LSP

Rectangle r = new Square();


[Link](5);
[Link](6);

[Link]
Violations
- Without the fix (height and width can be changed independently), Square would be in
an invalid state when used as a Rectangle;
- With the fix (height and width are always the same), the Square setter methods weaken
(violate) Rectangle’s post conditions, which state that dimensions can be modified
independently
Liskov Substitution Principle - LSP
What we’ve learnt?
Ø A model cannot be validated in isolation.
Ø Classes, relationships and behaviour should be modelled according to how they are
used by client classes.
Ø Design principles serve as a strong guideline

public class Square {


double size;

public Square(double size) {


[Link] = size;
}
public double getSize() {
return [Link];
}
public void setSize(double size) {
[Link] = size;
}
}
Interface Segregation Principle - ISP
Interface Segregation Principle - ISP

Role interface >>


Dependency Inversion Principle - DIP
• High-level modules should not depend on low-level modules. Both should depend on abstractions
• Abstractions should not depend upon details. Details should depend upon abstractions

Violation

E D
X E
P
E E
C N
U D
T E
N
I C
O I
N E
S

What if we want to write it to disk? How can we reuse Copier?


Dependency Inversion Principle - DIP
High and low-level modules now depend on an abstraction

E
X
E
C
U
T
I
O
N

D
E
P
E
N
D
E
N
C
I
E
S
Balanced Abstraction Principle (BAP)
Software constructs (classes, modules, functions, etc)
grouped by a higher-level construct should be on the
same level of abstraction.

That means:
• All instructions inside a method should be at the same
level of abstraction.
• All public methods inside a class should be at the
same level of abstraction.
• All public classes inside a module
(package/namespace) should be at the same level of
abstraction.
• All sibling modules inside a parent module should be at
the same level of abstraction
• All components, services, etc.
Multiple levels of abstraction (instructions)

public List<Trip> allTripsIfFriends (User user, User loggedInUser)


throws UserNotLoggedInException {
if (loggedInUser == null) {
throw new UserNotLoggedInException();
}

return [Link](loggedInUser)
? [Link](user)
: new ArrayList<Trip>();
}
Single levels of abstraction (instruction level)

public List<Trip> allTripsIfFriends(User user, User loggedInUser)


throws UserNotLoggedInException {
validate(loggedInUser);

return [Link](loggedInUser)
? tripsBy(user)
: noTrips();
}

Extract instructions in lower level of abstraction into methods and name them using
Language in a higher level of abstraction.
Multiple levels of abstraction (method level)

public class PaymentService {

public void makePayment(PaymentDetails paymentDetails);

public boolean isValid(Card card);

public List<PaymentMethod> paymentMethodsFor(Country country);

public PaymentDetails toPaymentDetails(Json paymentDetailsJson);

public Json toJson(PaymentDetails paymentDetails);


}
LCOM4 – Lack of Cohesion of Methods

God object >>


Single levels of abstraction (method level)
Includes PaymentMethod
public class PaymentService {
and Country

public void makePayment(PaymentDetails paymentDetails);

public List<PaymentMethod> paymentMethodsFor(Country country);

public class CardService {

public boolean isValid(Card card);

public class PaymentDetailsJson

public PaymentDetails toPaymentDetails(Json paymentDetailsJson);

public Json toJson(PaymentDetails paymentDetails);

}
Multiple levels of abstraction (class level)

public class PaymentService {

public void makePayment(PaymentDetails paymentDetails);

public class Card {

public boolean isValid();

public class PaymentDetailsJson

public PaymentDetails toPaymentDetails(Json paymentDetailsJson);

}
Outside-In TDD
Outside-In TDD: Mocking
AT
B’ C
A

B’’
DB

UT UT
UT
B’ C
B’’ C A

B’’ IT

B’’ IMDB
Outside-In TDD & Design
Implement a console-based social networking application (similar to Twitter) satisfying
the scenarios below.

Features
Posting: Alice can publish messages to a personal timeline

> Alice -> I love the weather today


> Bob -> Damn! We lost!
> Bob -> Good game though.

Reading: I can view Alice and Bob’s timelines

> Alice
I love the weather today (5 minutes ago)
> Bob
Good game though. (1 minute ago)
Damn! We lost! (2 minutes ago)
Outside-In TDD & Design
Following & Wall: Charlie can subscribe to Alice’s and Bob’s timelines, and view an
aggregated list of all subscriptions

> Charlie -> I'm in New York today! Anyone want to have a coffee?
> Charlie follows Alice
> Charlie wall
Charlie - I'm in New York today! Anyone want to have a coffee? (2 seconds ago)
Alice - I love the weather today (5 minutes ago)

> Charlie follows Bob


> Charlie wall
Charlie - I'm in New York today! Anyone wants to have a coffee? (15 seconds ago)
Bob - Good game though. (1 minute ago)
Bob - Damn! We lost! (2 minutes ago)
Alice - I love the weather today (5 minutes ago)
Outside-In TDD & Design
Details
The application must use the console for input and output.
Users submit commands to the application. There are five commands:

posting: <user name> -> <message>


reading: <user name>
following: <user name> follows <another user>
wall: <user name> wall
exit: exit

“posting”, “reading”, “following” and “wall” are not part of the commands; commands
always start with the user’s name.

Don't worry about handling any exceptions or invalid commands. Assume that the user
will always type the correct commands. Just focus on the sunny day scenarios.
Don’t bother making it work over a network or across processes. It can all be done in
memory, assuming that users will all use the same terminal.
Non-existing users should be created as they post their first message. Application
should not start with a pre-defined list of users.
Proposed solutions:
Commands or Queries?
Application Flow (queries)
title Application flow [QUERIES]
Twitter->+Twitter: userCommand != QUIT
Twitter->+View: userCommand()
View->Console: print("> ")
View->+Console: readLine()
Console-->-View: String
View-->-Twitter: String
Twitter->+CommandExecutor: execute(userCommand)
CommandExecutor->+CommandFactory: commandFor(userCommand)
CommandFactory-->-CommandExecutor: Command
CommandExecutor->+Command: execute()
Command-->-CommandExecutor: Optional[List[Post]]
CommandExecutor-->-Twitter: Optional[List[Post]]
Twitter->+View: display(posts)
View->+PostFormatter: formatPosts(List[Post])
PostFormatter-->+View: List[String]
View->View: forEachPost
View->Console: write(postLine)
PostCommand (query)
title Post Command [QUERY]
CommandExecutor->+PostCommand: execute(userCommand)
PostCommand->+UserRepository: newPost(userName, post)
note right of UserRepository: create or return user
UserRepository->UserRepository: userBy(userName)
UserRepository->Clock: currentTime()
UserRepository->*Post: create(userName, postMessage, currentTime)
UserRepository->UserRepository: storePost
PostCommand-->-CommandExecutor: None
ReadCommand (query)
title ReadCommand Flow [QUERY]
Twitter->+Twitter: userCommand != QUIT
Twitter->+View: userCommand()
View->Console: print("> ")
View->+Console: readLine()
Console-->-View: String
View-->-Twitter: String
Twitter->+CommandExecutor: execute(userCommand)
CommandExecutor->+CommandFactory: commandFor(userCommand)
CommandFactory-->-CommandExecutor: Command
CommandExecutor->+ReadCommand: execute()
ReadCommand->UserRepository: postBy(user)
ReadCommand-->-CommandExecutor: Optional[List[Post]]
CommandExecutor-->-Twitter: Optional[List[Post]]
Twitter->+View: display(posts)
View->+PostFormatter: formatPosts(List[Post])
PostFormatter-->+View: List[String]
View->View: forEachPost
View->Console: write(postLine)
Application Flow (commands)
title Application flow [COMMANDS]

Twitter->+Twitter: userCommand != EXIT


Twitter->+UserInput: readLine()
UserInput-->-Twitter: userCommand
Twitter->CommandExecutor: execute(userCommand)
CommandExecutor->+CommandFactory: create(userCommand)
CommandFactory-->-CommandExecutor: Command
CommandExecutor->Command: execute
PostCommand Flow (command)
title Post Command [COMMAND]
UsersCommand->+PostCommand: execute(userCommand)
PostCommand->+UserRepository: newPost(userName, post)
note right of UserRepository: create or return user
UserRepository->UserRepository: userBy(userName)
UserRepository->Clock: currentTime()
UserRepository->*Post: create(userName, postMessage, currentTime)
UserRepository->UserRepository: storePost
ReadCommand Flow (command)
title ReadCommand Flow [COMMAND]

Twitter->+Twitter: userCommand != EXIT


Twitter->+UserInput: readLine()
UserInput-->-Twitter: userCommand
Twitter->CommandExecutor: execute(userCommand)
CommandExecutor->+CommandFactory: create(userCommand)
CommandFactory-->-CommandExecutor: Command
CommandExecutor->+ReadCommand: execute
ReadCommand->UserRepository: postsBy(user)
ReadCommand->PostFormatter: formatPosts(List[Post])
ReadCommand->ReadCommand: forEach post
ReadCommand->Console: write(postLine)
Queries Commands

Image: [Link] Image: [Link]


Twitter File: [Link] File: [Link]

Image: [Link] Image: [Link]


PostCommand File: [Link] File: [Link]

Image: [Link] Image: [Link]


ReadCommand File: [Link] File: [Link]
Discussion
Outside-In TDD
Day 1 Retrospective
Agenda
Day 1
• TDD & Naming
• Emergent design via TDD
• SOLID & BAP
• Outside-In TDD & Design

Day 2
• Interaction Driven Design (IDD)
• IDD Modeling
• Layers, hexagons, features, and components
• Identifying services from business rules
• Deriving architecture through Impact Mapping
Introduction to
Interaction Driven Design - IDD
Modeling Requirements using IDD

• In groups, think and discuss the business requirements of


a business application. It can be something you are
currently working on
• List the core and more meaty features
• Map them using IDD / DDD building blocks using your IDE
• Create all the packages/namespaces
• Create class and interface skeletons
• Create empty public methods
• Think about delivery mechanisms and integration with
external systems

TIP: Sequence diagrams can be very helpful here.


A few ideas
• A university system where students can enroll in different
disciplines. We need to create schedule, assign
professors, credits, etc.
• A radio station where the playlist is formed by pre-
arranged content, commercials, and user-based. How
many different ways users can ask for songs?
• Some sort of social network around a specific subject
(travelling, photography, sports.)
• Booking platform (hotels, cars, flights.)
• E-Commerce (with multiple payment methods and delivery
methods per country.)
• IoT and Robots.
Think about integration with other systems, with data coming in and going out.

TIP: Sequence diagrams can be very helpful here.


Present your solution
Layers, hexagons,
features, and components
Simon Brown - [Link]
What type of architecture do they represent?
How different are they from each other?
What about now?
Don’t only rely on organisation.
Design using encapsulation.
Identifying services from
business rules

[Link]
[Link]
Online fashion store
## Roles
**Client Product Owner:** Me
**Consultants:** You

## Goal

Your task is to model my online fashion store. Given a few scenarios (user journeys) and
some details of my business process, your will need to:

* Identify different functional areas (bounded contexts)


* Create sequence diagrams mapping responsibilities and collaboration of functional
starting from the browser, when user navigates to a page.

At the end, we should have a list of candidate functional areas with respective
responsibilities and dependencies.

NOTE: Do not take performance, caches, or any operational requirements into account.
Just focus on the functional requirements.
Online fashion store
## Business background

We are a famous fashion brand that sell clothes all over the world. Every season our
designers and fashion experts update our catalogue. Catalogues are country specific,
displaying a sub set of all the products we have. When our customers come to our website,
they see the catalogue according to the country they are in. Only products that are
available in that country are displayed. Pricing, tax and promotion are also country specific.
We have suppliers in different parts of the world who produce some of our products and
not all products are stored in all warehouses, making them unavailable in certain
countries. Our pricing specialists define the pricing of all products. Product price and tax
varies from country to country. We also have marketing specialists who create promotions
that can affect one or multiple countries.

When our customers come to our website and see our catalogue, the should only see the
products that are available in the country they are in. They should see the product name,
price, description, photos, videos, and any promotional discounts the product might have.
Due to cultural differences, we display different photos and videos of the same product in
different countries. Photos and videos are created by our studio professionals.
## Business background (cont.)

Customers should be able to products to their basket. We have a commitment to honour


any promotion a product might have as soon as the product is added to the basket. That
means that we will apply the discount even if the customer does the checkout after the
promotion is over.

There are different things that impact a product availability: a) not suitable to local market
(due to local fashion preferences, cost, quality standards, regulations, etc); b) the
warehouse where the product is, including quantity in stock; c) the different distribution
mechanisms available for a specific product, in a specific area. Some products might not be
eligible for certain delivery options. When buying multiple products, not all products might
be delivered at the same time and with the same delivery mechanism. This should be
clearly shown to the client.

When viewing the basket, all the products in the basket alongside their name, description,
thumbnail, availability, price, tax, promotion, and the total price. They should also be able
to see a list of recommended products according to the items in the basket. For now the
recommendation system should only take data from into previous orders but in the future
we would like to have a machine learning algorithm. We already started conversations
with an external company for that.
## Business background (cont.)

When the customer navigates to the checkout, they should see their default delivery
address, the content of their basket including product info, availability of each item, price
and tax, any promotion they might be entitled, delivery options, and the payment
methods. The delivery options and payment methods will take into account the default
delivery address in the customer profile. Products should be divided in groups, with
different delivery options according to the warehouse they are stored and quantity in
stock. E.g. One product can be delivered next day but two other products will be delivered
in 4 days using a different transport company. Some delivery types like same day delivery
or next day delivery are only available in a few countries. Delivery is outsourced to local
companies. Payment methods also vary from country to country and we use different
payment gateways around the world. E.g. some countries we support PayPal while other
countries we do not. Contracts with delivery companies and payment gateways are
handled by different departments.
Task 1 – Functional design
Create sequence diagrams representing the following flows:

1. View catalogue: Customer goes to the main page and sees the product catalogue
2. View basket: Customer checks the content of the basket. Imagine that the basket
already has a few products added to it.
3. View checkout: Customer goes to the checkout page and sees all the info described
above.

Remember:

• Identify different functional areas (bounded contexts)


• Create sequence diagrams mapping responsibilities and collaboration of functional
areas starting from the browser, when user navigates to a page.

At the end, we should have a list of candidate functional areas with respective
responsibilities and dependencies.

NOTE: Do not take performance, caches, or any operational requirements into account.
Just focus on the functional requirements.
Task 2 – Functional dependencies
1. Identify 1st and 2nd level functional areas. First level is public exposed while Second
level is only used by other functional areas.
2. Map which functional areas dependencies
3. Which ones should be combined into a single service and which ones should be
independent services?
Presentations
Real case study
View catalogue: [Link]
View basket: [Link]
View checkout: [Link]
Deriving architecture through
Impact Mapping
What we covered
Day 1
• TDD & Naming
• Emergent design via TDD
• SOLID & BAP
• Outside-In TDD & Design

Day 2
• Interaction Driven Design (IDD)
• IDD Modeling
• Layers, hexagons, features, and components
• Identifying services from business rules
• Deriving architecture through Impact Mapping
Retrospective
What did you learn?
What did you like?
Is there anything you can apply tomorrow?
Anything you didn’t like?
Any general comments?
Any suggestions?
[Link]
Thank you
sandro@[Link]
@sandromancuso

[Link]

You might also like