The document discusses test-driven development (TDD) and simple design. It introduces TDD and some of its core practices, including test-driven development, simple design, refactoring, and pair programming. It provides an agenda for a workshop that will cover these topics over three sessions, including extreme programming (XP), the four elements of simple design, test doubles, outside-in TDD, and SOLID principles. The workshop aims to communicate best practices for using technical practices to succeed with agile development.
Engineering Mechanics Chapter 5 Equilibrium of a Rigid Body
TDD and Simple Design Workshop - Session 1 - March 2019
1. TDD and Simple Design
A workshop on the core practices to sustain an Agile development
Paulo Clavijo Esteban (@pclavijo)
v1.3 - March 2019
Bengaluru, India
2. Software Engineer at Dell-EMC
Organizer Cork Software Crafters
Cork Software Crafters
Paulo Clavijo Esteban @pclavijo
paucls.wordpress.com
github.com/paucls
twitter.com/pclavijo
About me
3. Why Agile teams fail?
“ There's a mess I've heard about with quite a few projects recently. It works out
like this:
- They want to use an agile process, and pick Scrum
- They adopt the Scrum practices, and maybe even the principles
- After a while progress is slow because the code base is a mess ” - Martin Fowler, 2009
https://martinfowler.com/bliki/FlaccidScrum.html .
You must have good technical practices
to succeed with Agile!
4. “Continuous attention to technical excellence
and good design enhances agility.” - Agile Manifesto
7. XP Technical Practices
Team’s practices evolve
- User Stories
- Retrospectives
- Continues Delivery
- Front-end first
- UX
- Domain-Driven
- Monitoring
- BDD
...
But the core XP practices are
still as relevant as ever.
8. XP Technical Practices
Test-Driven Development
● Classic TDD
● Test doubles
● Outside in TDD
Simple Design
● 4 Rules of Simple Design
● Design principles
● Implementation patterns
● Design patterns
● Domain-Driven Design
Refactoring
● Code Smells
● Refactoring patterns
● Working with legacy code
Pair Programming
● Driver-navigator
● Ping-pong
● Pomodoro
10. Agenda
Session 1
● XP
● Classic TDD
● Pair-Programming
● TDD Good Habits
● 4 elements of simple design
Session 2
● Refactoring introduction
● Code Smells
● Design Principles introduction
Session 3
● Test Doubles
● Outside-in TDD
● SOLID Principles
...
13. “I’m sorry but you came to the wrong place:
this is not about testing” - Jason Gorman
14. TDD is more than having Tests …
Is what we do, a technique and attitude,
discipline on the design and development flow.
15. Why do TDD?
- Over time, code deteriorates and becomes harder to modify.
- Fear of Changing Code :-(
- Productivity goes down, tech-debt and estimations go up, ...
- Following TDD we’ll have a descriptive and trustworthy suit of tests.
- It helps us to write code that is clean, testable and more simple.
- It stops us from writing what we don’t need.
- We have always code that was green just minutes ago, no time is wasted debugging.
- Its quick feedback loops help to improve our design and make it easier to change.
16. Why doesn’t everyone do TDD?
Confusion and misunderstandings
- TDD takes longer than not doing TDD.
- We tried but it didn’t work.
- You can’t write tests until you know the design, and you can’t know the design until
you implement the code.
- Management doesn’t allow us!
Some thoughts
- There is a learning curve associated with learning TDD.
- TDD is extremely disciplined.
- Lack of design skills.
- It is harder to introduce with legacy code.
18. TDD - Red, Green, Refactor
1. Write a unit test, watch it fail
2. Write just enough code to make it pass
3. Improve the code without changing behaviour
Wri a l
te
“We write our tests before we write the code. Instead
of just testing to verify our work after it’s done, TDD
turns testing into a design activity. We use the tests
clarify our ideas about what we want the code to do.”
GOOS - Steve Freeman, Nat Pryce
RED
GREENREFACTOR
Mak as
Im ov
t e d
19. The Golden Rule of TDD
"Never write a line of code without a failing test”
Kent Beck
http://wiki.c2.com/?NeverWriteaLineOfCodeWithoutaFailingTest
20. Make it pass
What is the simplest code I can think to make the test pass?
21. Make it pass
Green Patterns, different ways to implement the logic to make our test
pass:
● Fake it (until you make it).
● Obvious implementation.
● Triangulation.
22. The 3 Laws of TDD
1. You are not allowed to write any production code unless it is to make a failing unit
test pass.
2. You are not allowed to write any more of a unit test than is sufficient to fail; and
compilation failures are failures.
3. You are not allowed to write any more production code than is sufficient to pass the
currently failing unit test.
Robert C. Martin
http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd
24. Baby Steps
TDD breaks coding down into “Baby Steps”, bringing more focus to
every line of code we write and highlighting more errors that we
might have missed taking bigger steps.
25. Baby Steps
“Remember, TDD is not about taking teeny-tiny steps, it's
about being able to take teeny-tiny steps. Would I code
day-to-day with steps this small? No. I needed to remind
myself of this from time-to-time” - Kent Beck
26. Classic TDD workflow
Source: Rachel M. Carmena @bberrycarmen
It can be useful to commit each step if you want to undo changes easily.
27. One thing at a time
● Don’t jump from behavior to behavior before one is completely
implemented.
● Test one thing at a time, this is one behaviour or one rule.
● Use the test name to clearly describe that rule.
30. Arrange, Act, Assert
public class GreeterTest {
@Test
public void should_say_hello() {
// Arrange
Greeter greeter = new Greeter("John Doe");
// Act
String greeting = greeter.sayHello();
// Assert
assertThat(greeting).isEqualTo("Hello, John Doe!");
}
}
public class GreeterTest {
@Test
public void should_say_hello() {
Greeter greeter = new Greeter("John Doe");
String greeting = greeter.sayHello();
assertThat(greeting).isEqualTo("Hello, John Doe!");
}
}
31. @Test
public void should_say_hello() {
// Arrange
Greeter greeter = new Greeter("John Doe");
// Act
String greeting = greeter.sayHello();
// Assert
assertThat(greeting).isEqualTo("Hello, John Doe!");
}
Writing the assertion first
It is a good habit to start writing a test from the bottom.
1) What I expect to happen? → Assert
2) How should I trigger this? → Act
3) What minimun setup/preconditions do I need to have all in place? → Arrange
// Arrange
Greeter greeter = new Greeter("John Doe");
// Act
String greeting = greeter.sayHello();
fromthe“what”tothe“how”
32. How many assertions do I need?
Follow the “Single Assert Rule”, but apply it to logical assertions.
physical assertion <> logical assertion
@Test
fun `should not contain duplicated tags`() {
// Given
val post = Post()
post.addTag("Sport")
post.addTag("Travel")
post.addTag("Travel")
// When
val tags = post.getTags()
// Then
assertThat(tags).hasSize(2)
assertThat(tags[0]).isEqualTo("Sport")
assertThat(tags[1]).isEqualTo("Travel")
}
@Test
fun `should not contain duplicated tags`() {
// Given
val post = Post()
post.addTag("Sport")
post.addTag("Travel")
post.addTag("Travel")
// When
val tags = post.getTags()
// Then
assertThat(tags).containsExactly(
"Sport", "Travel")
}
@Test
fun `should not contain duplicated tags`() {
// Given
val post = Post()
post.addTag("Sport")
post.addTag("Travel")
post.addTag("Travel")
// When
val tags = post.getTags()
// Then
assertThat(tags).doesNotHaveDuplicates()
}
33. Use them to make your tests more expressive, raising the level of abstraction to talk in
terms of the domain and less about implementation details. A custom DSL!
Fluent assertions, Custom assertions ...
class PersonTest {
@Test
fun using_custom_assertions() {
val person = Person(name = "Alice", age = 21)
assert(person).hasValidName()
assert(person).isAdult()
}
// AssertK Custom assertions
fun Assert<Person>.hasValidName() {
if (actual.name.isNotBlank()) return
expected("Name must not be blank")
}
fun Assert<Person>.isAdult() {
if (actual.age >= 18) return
expected("Age must not be below 18")
}
}
34. Principles of good Unit Tests
[F]ast
[I]solated
[R]epeatable
[S]elf-validating
[T]imely / Thorough
37. Pair Programming
“Ping Pong” Pairing
Partner A Partner B
Write a test, watch it fail
Make it pass
Improve the code
Write next test, watch it fail
Make it pass
Improve the code
Write next test, watch it fail
RED
REFACTOR GREEN
39. TDD good habits https://github.com/neomatrix369/refactoring-developer-habits
40. TDD good habits
Principles
● tests should test one thing only
● each test should be self-contained, including
data
● ensure tests are independent of each other
● don't refactor with a failing test
● organise your unit test projects to reflect
your production code
● keep your tests and production code
separate
● if your tests are difficult to write or maintain,
consider changing the design
41. TDD good habits
Red phase
● create more specific tests to drive a more
generic solution (Triangulate)
● give your tests meaningful names (behaviour
/ goal-oriented) that reflect your production
system
● organize your test in Arrange, Act and Assert
blocks
● write the assertion first and work backwards
● see the test fail for the right reason
● ensure you have meaningful feedback from
failing tests
42. TDD good habits
Green phase
● write the simplest code to pass the test
○ write any code that makes you get to
the refactor phase quicker
○ it is okay to write any code that you
might improve at a later stage
● consider using Transformation Priority
Premises to evolve your code
43. TDD good habits
Refactor phase
● refactor aggressively and constantly
● treat tests as first class code
● use the IDE to refactor quickly and safely
● refactor production and test code
independently (except changing public
interfaces)
● Use the Rule of 3 to tackle duplication
● Remember that duplication is cheaper than
the wrong abstractions
45. Practice Retrospective
● Did you ever write more code than you needed to make the current tests pass?
● Did you ever have more than one failing test at a time?
● Did the tests fail unexpectedly at any point? If so, why?
● How did you choose the order of test cases to implement?
● How much did writing the tests slow you down?
● Did you write more tests than you would have if you had coded first and written tests
afterwards?
● Are you happy with the design of the code you ended up with? Should you have refactored it
more often?
46. 4 Rules of Simple Design
1. Passes the tests
2. Reveals intention
3. No duplication
4. Fewest elements
https://martinfowler.com/bliki/BeckDesignRules.html
47. Duplication and the “Rule of three”
1, 2, 3!
“Duplication is far cheaper than the wrong abstraction” - Sandy Metz
48. Parameterized tests
A useful pattern to consolidate repetitive test methods that only differ in terms of input/output values.
NUnit TestCase Attributes
51. Learning materials (Session 1)
- Read Test Driven Development: By Example, Kent Beck
- Read Test-driven development on Wikipedia
- Read The three rules of TDD, Robert C. Martin
- Read "The Three Laws of TDD" on chapter 9 of Clean Code
- Read "Single Concept per Test" on chapter 9 of Clean Code
- Watch The 3 Laws of TDD: Focus on One Thing at a Time, Jon Reid
- Read Do More With Baby-Steps TDD, Oleksii Fedorov
- Read Pair Programming – The Most Extreme XP Practice?, Dave Farley
- Read Pair Programming for Introverts, Dave Farley
- Read 21 ways to hate pair programming, William Pietri
- Read The Four Elements of Simple Design, J.B. Rainsberger
- Watch “Episode 6: TDD” of Clean Coders
- Read Why do You Want Me to Write Bad Code, David Tanzer
- Read The Mysterious Art Of Triangulation, Jason Gorman
- Read The Transformation Priority Premise, Robert C. Martin
- A recomended course The World's Best Intro to TDD, J. B. Rainsberger
- Practice doing the Leap Year Kata
- Practice doing the Roman Numerals Kata
- Practice doing the Prime Factors Kata