TDD is not JUST

Fabrizio Romano

@gianchub

Skillsmatter - London

October 21th, 2015

Hello London!

Thank you for being here!

I'm a Computer Science Engineer, addicted to Python, clean code and books.

I'm also writing my own, it's going to be out in Jan. 2016.

The plan:

What drew me to TDD?

Why do we need it?

...and let's have some fun while we do it, ok?

A little disclaimer

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

What drew me to TDD?

It all happened when I moved to 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([]))``````

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...

Kent Beckowl

Robert Martinowl

TDD strooong

in them was...

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
• Architecture design during refactoring
• Three strikes and refactor
(Test-Driven Development with Python - H. Percival)
• 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(**search_params)

# here 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(**search_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.

return result``````

One possible solution:

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

result = very_long_function(*args, **kwargs)

eq(expected_result, result)

def very_long_function(*args, **kwargs):

# same nasty piece of code
# with NEW FUNCTIONALITY IN

return result``````

Thank you! Come say hi!

gianchub

gianchub

gianchub [at] gmail [dot] com

TDD is not just about tests (SM London)

By Fabrizio Romano

TDD is not just about tests (SM London)

Skillsmatter talk: TDD is not just about tests.

• 2,153