@gianchub
ProgSCon - London
April 22th, 2016
I'm a Computer Science Engineer, addicted to Python, clean code and books.
I'm the author of Learning Python (Packt Publishing, 12.2015).
Sr. Platform Developer
@ student.com
We're
hiring!
...and let's have some fun while we do it
It all happened when I moved to London...
Don't worry, TDD will take us there
- Mark Henwood
Too much logic!
- Ondrej Kohout
A couple of colleagues were a great inspiration:
def is_positive(n):
# We assume n is integer.
return n > 0
How do we test this function?
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
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...
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.
def get_squares(v):
# assumes v is a list of integers
if not v:
return []
return [n ** 2 for n in v]
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]
# this test would cause us to write get_squares2
eq([1, 0, 4, 9], get_squares2([-1, 0, 2, -3]))
# and this would automatically pass, thanks to
# the list comprehension
eq([], get_squares2([]))
Had we written the tests first, there would be no redundancy.
def get_squares2(v):
# assumes v is a list of integers
return [n ** 2 for n in v]
Psst: It's extremely technical, beware!
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!
TDD?
Let me get back
to you on this...
Kent Beckowl
Robert Martinowl
TDD strooong
in them was...
TDD is a Software Development Process based on the repetition of a very short development cycle.
Def:
OMG! It's like
having 2 brains!
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.
def get(request, *args, **kwargs):
# imagine many lines of code here...
data = get_data(**search_params)
# here we work on the data and
# eventually we put it in the context
# dict we use to render the template
return render(template_name, context, extra_params)
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)
def very_long_function(*args, **kwargs):
# nasty piece of code that does
# a lot of things.
return result
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
gianchub
gianchub
gianchub [at] gmail [dot] com