0% found this document useful (0 votes)
4 views

Coach Denis - Test-driven Development 2023 (Re 2)

Uploaded by

Jason Cruz
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)
4 views

Coach Denis - Test-driven Development 2023 (Re 2)

Uploaded by

Jason Cruz
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
You are on page 1/ 72

The No-Nonsense

Test-Driven Development
Booklet
By Denis Čahuk, Revision 2
Who is this booklet for?
This booklet is best suited for software engineers,
developers, technologists, tech leaders and managers as well
Introduction as students and amateurs wanting to step into this field.

What will you find inside?


Distilled information about the core concepts a team or an
individual adopting Test-driven development requires. Mix it
with Domain-driven Design, how to convince your manager,
how to inspire your team if you're the manager. Includes
practical advice from challenges directly from the tech
industry and finishing with my coaching and advisory
offering.
How can you help?
Share it with your friends and coworkers, leave a review on
gumroad. You can come share your findings with me, the
author on linkedin. Come join us on the Technologist
Podcast for updates.
Chapter 1

Mistaking Learning TDD


for Doing TDD
You’re not doing it wrong.

Software design is just hard.

Good design is very hard.


We tend to find that things that
make code easier to change tend
to also make it more reliable.
Jason Gorman
The Technologist Podcast, Ep. 9
It takes time.

You slow down while


exploring and learning.

Sometimes it's not a good fit


and your team abandons it.

That's fine too.


Chapter 2

The productivity myth /


why learn TDD?
What does the business
want?
⭐ To provide business value to users with as little code as possible
while maintaining quality
How do we achieve that?
⭐ By focusing on delivering business outcomes,
gathering feedback often
while keeping accidental complexity to a minimum

🤖 Maintainability + Testability = Quality


🔥 Low quality = Hard to change = Tech Debt 😱
Tech Debt
⭐🔥😱 Tech Debt is the expression of bad Code Health given the metrics
your organization choses to measure Code Health by.

Features within unhealthy code contexts may take an order of


magnitude longer to develop

Predictors of High Tech Debt / Low Code Health, code smells:


Low Cohesion, Brain Classes, Large Classes, Brain Methods, Nested if statements,
Complex - centralised functions, copy-pasted logic, primitive obsession, lack of domain
language (too much tech or framework jargon)

Codesense blog post & tech debt research whitepaper


Testing becomes expensive when
the existing code resists re-design
and simplification.
TDD helps your team address
the fear of making changes while retaining testability
It's trivial to add a second bathroom to a
house if you're planning for it from the
beginning.

If not - oh boy you're in for a renovation!

Writing tests after implementation is the


same.
TDD helps you...
increase modularity
retain testability
and cohesion

go home knowing your increase trust in your


code works passing tests
Chapter 3

The TDD Cycles


😕 😐 😵
Red Green Refactor

In plain English please?!


1 2 3

Understand Explore and Generalize,


expected transform the Clean up
behavior implementation
Imagine you are automating
debugging your finished
system from the perspective
of a user
1 Understand

Write down what the user expects to see

This is your test.

Important: Start with your least complex test first and iterate by gradually
increasing complexity - this step is critical for reducing unnecesary, accidental
over-architecting
2 Explore

Implement the most obvious, simple solution to make the test


pass.

Nothing more.
If 'return 5' passes all tests, then write that.

This step is a transformation that changes the public interface and


methods. Keep this step under 5 minutes.
2 Explore, cont'd.

❌ Optimized is not obvious and simple


❌ Staring at the screen for 20 minutes is not obvious and simple
❌ Perfectly analyzed is not obvious and simple
3 Generalize

Design improvement:

Tests should be specific.

Implementation should be general

This step changes the (private) implementation or API internals


3 Generalize, cont'd

Refactor

✔ Simplify your test code


✔ Simplify your implementation code

🤚 Changing the public API of your method, class or module is NOT refactoring.
🔥 𝐘𝐨𝐮 𝐜𝐚𝐧 𝐭𝐡𝐫𝐨𝐰 𝐭𝐡𝐞 𝐬𝐢𝐥𝐥𝐲 𝐞𝐚𝐫𝐥𝐲 𝐭𝐞𝐬𝐭𝐬 𝐚𝐰𝐚𝐲
🔧 Simplify tests as part of the refactoring step

Keep the critical ones and ditch the ones you


think will get in your way.

TDD is primarily an abstraction methodology that


retains code testability

Second, it 's a debugging automaton tool.

Third - and only rarely - does it actually design


something out of thin air.
Key 1 If it's not worth testing, it's
probably not worth coding

Principles 2 Create abstractions only when


refactoring
3 Write only the simplest code to
pass your tests
4 Write tests in the language of
the user / business
5 Test entire feature modules, not
classes
Remember, modular software design is hard

TDD does not replace the engineer with


magic

TDD is a tool you use to move smoothly,


without fear and retain testability
Chapter 4

Overcome the fear and


resistance to changing
requirements
Modular code and Tests should only fail
behavior-oriented when the behaviour
tests do not break of an old feature
when adding new changed or
functionality or knowledge about it
changing has been accidentally
implementation lost / forgotten
😤 If there is a gas leak in your
house, you don't want the answer
to your obvious question to be
"Somewhere."
Jason Gorman
The Technologist Podcast, Ep. 9
Why bother? What’s my
benefit?
Testable, modular
code helps your non-
technical boss Managers value this
answer the question answer much more
than engineers.
"Is it working?"
Tests your team Tests your team
TDD
wrote trusts
Chapter 5

Embrace Modularity /
Tactical Domain Driven
Design
TDD x DDD
The design aspect of TDD is oft-overlooked.

TDD done well is a high bar if good design and domain understanding is
alien to the engineer.

Sadly, TDD doesn't (directly) address that off-the-bat.

Domain-driven design (or another domain language strategy) is a


mandatory complement to improving design pragmatically.
Domain-Driven Design - Core
Ubiquitous Language - Nouns and verbs your business uses to explain
processes in real conversation. Use these words instead of technical
jargon

Core Domain - Subject-matter topics that define your business problems


that you are writing code for.
ie. User Registration is Core Domain for Auth0 or Okta - but not for an E-
commerce application.

Bounded Context - A subdivision inside your Core Domain. Contexts do


not overlap. Entities and Aggregates always belong to only one Context.
Contexts appear when two parts of the business use the same noun in
different meanings (ie. an Order during Checkout vs. an Order during
Fulfillment)
Domain-Driven Design - Tactical
Entities - Objects with an identity that carry roles of behaviour. Imagine all
your processes are handled by humans. You would have an Entity for each
Role/Title a human can take on, even if it's a singleton (e.g. Cashier, Teller,
Customer, Accountant, ShipmentHandler, CEO).
Entities you usually don't want to test directly.

Aggregates - A behavioural module of Entities that change together,


usually within a transaction boundary. The outside-facing entity posing as
the public API is called the aggregate root.
These you do want to test aggressively, almost exclusively.

Aggregate Roots - The entry-point Entity representing an Aggregate


publicly.
Domain-Driven Design - Tactical II
Domain Services - behavioural glue for verbs and methods that don't
naturally fit into entities. Keep these to a minimum.

Repositories - Collections of Entities (think Set/HashSet, not SQL table)


that glue Infrastructure details to Domain Entities

Value Objects - object types using nouns from the ubiquitous language to
promote primitives and anonymous types into more concrete values.
Treat them as immutable and without identity (compare based on
attributes)

Domain Events - the expression of side effects within your domain. Can
be used for decoupled messaging and event sourcing.
Domain-Driven Design - Layers

Application
Communicates with Services, and Factories to access Aggregate Roots.
This is where frameworks are most practical.

Domain
This is where your revenue-producing business logic lives. Your main
TDD activity goes here. The bulk of your tests go here.

Infrastructure
Persistence, communication, databases, security. Contrary to popular
belief - if using a PostgreSQL is a business and domain decision,
abstract the connection from the domain, but not the type (or queries)
Why does all of this DDD
detail matter?
The ROI on adopting TDD is low maintenance
costs by providing high modularity and retain
testability.

High cohesion and low coupling are


necessary to maintain a continuous effort in
reducing complexity, while also keeping
code readable.

DDD patterns are a strong predictor of high-


level code organization to manage unwanted
complexity.
Chapter 6

Expensive habits to avoid


🥵 Don't test your classes/methods. Avoid Having
an XTest for Class X and a YTest for class Y.

😀 Instead, focus on capturing the entire


behaviour of the module:

"What does this API do?" Test that.

"What DTOs does this generate for the UI component?"


Test that.
A project without automated tests on a tight deadline.

✅ You move fast.


✅ You break things.
✅ You fix them.

😱 Writing tests will slow you down.


😱 TDD will slow you down.
😱 Refactoring and tech debt can wait.

Everything seems to go well.


You saved a lot of time not doing wasteful practices...
But...

😱 There's a bug you cannot explain.


😱 You are desperately trying to fix it before release.
😱 The bugfix hotfix invalidates all manual testing

😯 Does everything work?


--
🤔 You move fast.
😱 But you don't release.

Don't mistake speed for estimated arrival time.


The 2 Death Spirals
These habits 𝐤𝐢𝐥𝐥 𝐲𝐨𝐮𝐫 𝐭𝐞𝐚𝐦'𝐬 𝐩𝐫𝐨𝐝𝐮𝐜𝐭𝐢𝐯𝐢𝐭𝐲 𝐚𝐧𝐝 𝐦𝐚𝐢𝐧𝐭𝐞𝐧𝐚𝐧𝐜𝐞 𝐜𝐨𝐬𝐭𝐬.

Legacy code is everywhere. Starve it, rather than feeding it.


𝐓𝐡𝐞 𝐓𝐞𝐬𝐭𝐚𝐛𝐢𝐥𝐢𝐭𝐲 𝐒𝐩𝐢𝐫𝐚𝐥 🐔🥚🍳
"Chicken-egg problem"

🐔Your code is difficult to test


😱 It's hard to understand / Written by others
🔧Refactoring is hard due to lack of tests
🏃‍♀️Every sprint takes much longer than anticipated
🍳 𝘍𝘖𝘙 𝘜𝘕𝘒𝘕𝘖𝘞𝘕 𝘙𝘌𝘈𝘚𝘖𝘕𝘚

"But we don't waste time writing tests, why are we slow?"

Thus, you never have time to write tests 🥚


𝐓𝐡𝐞 𝐑𝐢𝐠𝐢𝐝𝐢𝐭𝐲 𝐒𝐩𝐢𝐫𝐚𝐥 📻🧀🔥
"Your tests are sticking to your code like melted cheese to a radio"

🧀 Your tests are rigidly coupled to implementation


❌Low quality tests fail when adding unrelated features
🙈So you let your tests fail because you no longer trust them
🏃‍♀️Every sprint takes much longer than anticipated
🔥𝘍𝘖𝘙 𝘜𝘕𝘒𝘕𝘖𝘞𝘕 𝘙𝘌𝘈𝘚𝘖𝘕𝘚

"But we have all these tests, why are we slow?"

Thus, you never have time to add features 📻


Chapter 7

Tips for beginners,


learning on your own
Make your life easier
👉do 2 passes for complex changes

1 2

understand add more tests


explore expand
skip refactoring refactor tests
💪 Practice using TDD Kata Dojos

💪 When solving leetcode challenges,


use the test cases to perform TDD-style
development
💡
✋ Avoid performing TDD against
libraries and framework modules

💪 Test your own code first that


integrates against it with smart
abstractions (ie. ORM)
💡
🤔 Transformation Priority Premise (TPP)

It is a prioritised list of transformations.

Transformations are the act of adding features and behaviour to the system.
Refactorings are the act of changing implementation without altering behaviour.

The list goes from simplest to most complex.

❓ Why is this important?


✨ Keep your code-writing cycles short.
✨ Understand the problem by writing a test,
Explore the implementation (e.g. using TPP or intuition),
Generalise by refactoring

☑ Instead, each step should be of similar increment sizes.


☑ TPP promises to help you stay along the simplest path by prioritising your next test
using the list.

1. ({} →
nil) no code at all code that employs nil

2. (nil constant)
3. (constant→ constant+) a simple constant to a more complex constant
4. (constant → scalar) replacing a constant with a variable or an argument
5. (statement → statements) adding more unconditional statements.
6. (unconditional → if) splitting the execution path

7. (scalar array)

8. (array container)
9. (statement → tail-recursion)

10. (if while)
11. (statement→ non-tail-recursion)
12. (expression → function) replacing an expression with a function or
algorithm
13. (variable → assignment) replacing the value of a variable.
14. (case) adding a case (or else) to an existing switch or if

source: TPP @wikipedia


Chapter 8

Tips for professionals


😟 Do you feel powerless against a
manager who doesn't allow you to try
or learn TDD?

👉
💚 Tests are the engineer's responsibility. It's about
cost-saving, safety and reliability.

When I get my hair cut, I don't want the hairdresser


to blame the manager for cutting half my ear off
due to the slippery floor.

👉
💚 When you're doing testing and you also want to
learn TDD, don't go all in.

Decide what percentage of your work will be


learning TDD. 1 hour a week is plenty. Pair up with a
co-worker, do it off-duty or find a mentor.

It may take you months or years to master TDD.


Treat it like a new language or framework.
👉
💚 If you have to ask permission to do the right thing,
you're in an environment that may negatively affect
your mental health and career growth.

These kinds of topics should be addressed on regular


standups, rather than on exit interviews.

👍
Chapter 9

Tips for Companies


🚀 Don't force TDD onto a sceptical team.

Start with the most enthusiastic adopter, one feature


at a time, a fraction of the workload.

TDD trainers are available to help you once you decide


TDD is what you are looking for to optimize your work.

The industry is polarized enough on hard-to-measure


complex processes.
🚀 Don't force 𝐡𝐢𝐠𝐡 𝐜𝐨𝐝𝐞 𝐜𝐨𝐯𝐞𝐫𝐚𝐠𝐞 𝐨𝐧 𝐥𝐞𝐠𝐚𝐜𝐲 𝐜𝐨𝐝𝐞𝐛𝐚𝐬𝐞𝐬. 𝐂𝐨𝐯𝐞𝐫
𝐜𝐫𝐢𝐭𝐢𝐜𝐚𝐥 𝐩𝐚𝐭𝐡 𝐟𝐢𝐫𝐬𝐭.

🚀 Invest into making code testable at the earliest stage of


implementation

🚀 Cover critical paths with expensive e2e tests first where


hard-to-test code has already been authored
This investment will:
💰 Minimize maintenance costs by reducing (needlessly
repeated) debugging hours by your most expensive
employees

💰 Maximize uptime and strive towards a zero-bug policy by


providing early feedback

💰 Enable your product team to reach market sooner by


accelerating adaptation to requirement and
implementation changes
🔥 Attacking Tech Debt

The best candidates for introducing TDD are your files,


classes and modules:

that have the highest change frequency


critical features that have only 1 author
with large LOC and contain code smells
Chapter 10

Resources
THE
Book
that started everything
About me
Hi, my name is Denis Čahuk, but
you may know me as Coach Denis.

I am a father, hands-on architect,


podcast host, technology
consultant and technical
craftsmanship coach.

I am a technology influencer and


youtuber on topics of TDD, DDD,
event-driven system, event I promised I'd update my picture.

sourcing.
I'm meeting with my photographer next week.
🎙 The Technologist
Podcast
The Technologist Podcast is where
we discuss all matters of being a
Technologist.

The tech stack, tools, process


paradigms, organizational structure
and the human element.

TDD, DDD, Event Sourcing and Check it out!


Modeling, Craftsmanship, Clean
Architecture, Agile are frequent
topics.
🚀 Hire me as Technical
Coach for TDD / Tech
Debt/ Architecture
🔗Connect with me on Linkedin, schedule an intro call for further inquiries

🔗For B2B services for your company, contact me directly via email
[email protected]
🔗Accelerate your mastery with my TDD workshop
🔗Hire me for long-term 1on1 Mentoring
Chapter 11

Thanks and
Acknowledgements
Thanks to the early adopters, supporters and enthusiasts who gave feedback and made
this booklet grow from the initial few pages:
·
Siddhart R. C. · Habibellah A. · Kishore K. A. · Dragos R. · Shahriyar R. · Ryan D. · Joost D. ·
Nikolina G. · Lucas L. · Peter S. · Slavomir P. · Deepak S. · Gourav S. · Hussein E. · Jacobus
M. · Fawwad K. · Edouard M. · R Kaja M. · Neel D. · Marcelo C. · Jaime B.C. · Kaivalya A. ·
Prashant B. · Sabahudin K. · Siddhart S. · Russel S. · Jason C. · Torben H. · Vladi I.

·
Special thanks to Nik Sumeiko, Jay Desai, Jason Gorman, Valentina Cupać, Richard
Dyce, Oliver Zihler, Taj Pelc, Jonathan Stark and Sander Hoogendoorn for the long
conversations and encouragement that made all of this possible.

·
Finally, I can't express in words the magnitude of joy, gratitude and inspiration I received
from my beautiful and courageous partner, Asja, and our two kids, Luna and Gaja, who
inspire me on a daily basis and support me so perfectly in opening up to even more
challenges and opportunities in my life.
I would love to hear your
success and failure stories
Got a hard-to-test system?
Shoot me an email: [email protected] 📧
Share this booklet with your
friends and coworkers!

You can use this link.

⭐Leave a review on gumroad


Thank you!

You might also like