# TDD is not JUST

### Fabrizio Romano

@gianchub

EuroPython 2015 - Bilbao

July 21th

# Hello Bilbao!

0110010101100110

Got it?

## The plan:

Hello! (Amazing joke about the bit...)

The plan (we're here)

What drew me to TDD?

Why do we need it?

A story about TDD

## A little disclaimer

• This is my own view
• Simple examples
• No definitions

# What drew me to TDD?

It all happened in London...

### Mark Henwood

Don't worry,

TDD will take us there

Too much logic!

# Why do we need TDD?

``````def is_positive(n):
# We assume n is integer.
return n > 0``````

# Example #1

How do we test this function?

## Boundaries

``````def is_positive(n):
# We assume n is integer.
return n > 0

eq(False, is_positive(0))
eq(False, is_positive(-3))
eq(True, is_positive(3))``````

Is this a good test?

``````def is_positive(n):
# We assume n is integer.
return n > 1

eq(False, is_positive(0))   # still passing
eq(False, is_positive(-3))  # still passing
eq(True, is_positive(3))    # still passing``````

## Granularity

``````def is_positive(n):
# We assume n is integer.
return n > 1

eq(False, is_positive(0))   # still passing
eq(False, is_positive(-1))  # still passing
eq(True, is_positive(1))    # NOW FAILS!``````

Much better!

The boundary cannot jiggle any more!

``````def is_positive(n):
# We assume n is integer.
return n > 0

eq(False, is_positive(0))

for n in range(1, 10 ** 4):
eq(False, is_positive(-n))
eq(True, is_positive(n))``````

Even better!

But unit tests need to be FAST...

# So, can we test everything?

``````def is_positive(n):
# We assume n is integer.
if n == 10 ** 16:
return 'Hola!'
return n > 0
``````

If you could test 1'000'000'000 numbers a second, it would take about 4 months to spot this.

## We cannot test everything.

``````def get_squares(v):
# assumes v is a list of integers
if not v:
return []
return [n ** 2 for n in v]``````

# Example #2

How do we test this function?

``````def get_squares(v):
# assumes v is a list of integers
if not v:
return []
return [n ** 2 for n in v]

eq([1, 0, 4, 9], get_squares([-1, 0, 2, -3]))
eq([], get_squares([]))``````

But what about that redundancy?
We may not notice it, if we tested AFTERWARDS

``````def get_squares(v):
# assumes v is a list of integers
if not v:
return []
return [n ** 2 for n in v]

def get_squares2(v):
# assumes v is a list of integers
return [n ** 2 for n in v]``````
``````# this would cause us to write get_squares2
eq([1, 0, 4, 9], get_squares2([-1, 0, 2, -3]))

# this would automatically pass, thanks to
# the list comprehension
eq([], get_squares2([]))``````

Had we written the tests first, there would be no redundancy.

# Once Upon a Time...

## there was a frog

He was in love with a beautiful princess. One day, at the pond, the frog took courage, jumped out of the water and told her:

I am a prince under a spell, kiss me,

break the spell and marry me!

Prince? Intriguing!

## But she was a Geek...

TDD?

Let me get back

to you on this...

### And he ran off to learn TDD from the masters...

Kent Becko

Robert Martiño

TDD was strooong in them...

# TDD

## Test Driven Development

TDD is a Software Development Process based on the repetition of a very short development cycle.

Def:

# Steps

• Short at first
• With experience: longer
• Trouble? Go back to short

# Where does it start?

OMG! It's like

having 2 brains!

## TDD common aspects

• KISS
• YAGNI
• Three strikes and refactor
(Test-Driven Development with Python - H. Percival)
• Architecture design during refactoring
• Triangulation

# ?

## Triangulation

``````eq(4, square(-2))

def square(n):
# we can cheat, it is
# the only requirement
return 4``````

First step

"Fake it 'till you make it"

``````eq(4, square(-2))
eq(9, square(3))

def square(n):
return n ** 2``````

With Triangulation

You write

the actual logic.

## Main Benefits

• Refactor with Confidence
• Loose Coupling
• Easier to test and maintain
• Test first => Better understanding of requirements
• Small units => easier debugging and tests as docs
• Higher speed:
It takes less to write tests and code
than to write code and debug

## Main Shortcomings

• Whole company needs to believe
• Blind spots
• Badly written tests are hard to maintain

# Changing a horribly long view

``````def get(request, *args, **kwargs):

# ...
# imagine many lines of code here...
# ...

data = get_data(**params)

# ...
# data is prepared, worked on
# put in the context dictionary
# and the view finally renders
# a template
return render(template_name, context, extra_params)``````

### We need to insert pagination, filtering, sorting

``````def get(request, *args, **kwargs):

# same as before

original_data = get_data(**params)

filtered_data = filter_data(
original_data, **filter_params)

sorted_data = sort_data(
filtered_data, **sort_params)

data = paginate_data(
sorted_data, **pagination_params)

# same as before

return render(template_name, context, extra_params)``````

# Introducing a new functionality in existing code

## We need to add a feature to a long piece of untested code.

``````def very_long_function(*args, **kwargs):

# nasty piece of code that does
# a lot of things.
# Uncle Bob would cry if he saw it...

return result``````

## One possible solution:

``````def test_new_functionality():
# preparation stage
# ...

result = very_long_function(*args, **kwargs)

assert_equal(expected_result, result)

def very_long_function(*args, **kwargs):

# same nasty piece of code
# with NEW FUNCTIONALITY IN
# Uncle Bob still unhappy

return result``````

# Thank you! Come say hi!

gianchub

gianchub

gianchub [at] gmail [dot] com

http://slides.com/gianchub/ep2015-tdd

#### TDD is not just about tests (Bilbao)

By Fabrizio Romano

# TDD is not just about tests (Bilbao)

EuroPython talk: TDD is not just about tests.

• 2,137