Skip to content

TDD

Learning objectives

  • Understand what TDD is
  • Understand why TDD is important
  • Practice TDD
For teachers

Prerequisites are:

  • Learners have practiced pair programming
  • Learners can do the git basic workflow on master

Teaching goals are:

  • Learners understand what TDD is
  • Learners understand why TDD is important
  • Learners have done multiple TDD cycles

Teaching form used:

  • Pair programming
  • master branch

Prior knowledge questions:

  • How do you grow/develop your code?
  • How do others grow/develop their code?
  • Why would it be important to have a formal way to grow/develop your code?

Lesson plan:

gantt
  title Lesson plan TDD 1
  dateFormat X
  axisFormat %s
  Pair programming: done, 0, 30
  Theory: extra, 30, 45
gantt
  title Lesson plan TDD 2
  dateFormat X
  axisFormat %s
  Exercise 1: crit, exercise_1, 0, 20s
  Feedback 1: feedback_1, after exercise_1, 10s
  Retrospect: crit, 30, 15s

Introduction

Test-driven development (TDD) is a systematic way to grow code, used in academia and industry. It works [Martin, 2011]!

A TDD developer

The TDD cycle

The TDD cycles end when you cannot break your function anymore [Beck, 2022][Langr, 2013].

Advantages of TDD are:

  • TDD makes developers more productive [Erdogmus & Morisio, 2005]
  • TDD increases quality of the code [Erdogmus & Morisio, 2005][Alkaoud & Walcott, 2018][Janzen & Saiedian, 2006]
  • There are plenty of costly programming mistakes documented!
  • TDD helps shape the project architecture [Mayr, 2005]
  • TDD helps better modularisation [Madeyski et al., 2010]

Developers do really do this [Beck, 2022][Langr, 2013], even though TDD takes longer (but note [study I cannot find]):

Study Extra time Effect
[George & Williams, 2004] 16% 18% more black-box tests pass
[Bhat & Nagappan, 2006] 15% 2x higher code quality
[Nagappan et al., 2008] 15-35% 40%-90% less defects

We will discuss formal testing later, but now note that testing is not about finding bugs [Thomas & Hunt, 2019, tip 66], instead a test is the first user of your code [Thomas & Hunt, 2019, tip 67].

Exercises

Technical rules

  • Use the GitHub repository for the learners of this course,
  • Work on the main branch
  • Work in a file called learners/[your_name]/[class_name].py, where [your_name] is the person with first name first in alphabet
  • Zen Of Python: 'Errors should never pass silently'

Social rules

  • Ping-Pong Pair programming
  • Discuss how and when to switch roles first!
  • Person with first name first in alphabet starts
  • Try to be an exemplary duo

Exercise 0: is_zero

Learning objectives
  • First practice of TDD by re-doing a function that has been developed

Develop the function is_zero with the technical and social rules showed at 'Exercises'.

is_zero

  • Function name: is_zero
  • Output:
  • Returns True if the input is zero
  • Returns False if the input is not zero
  • ⚠ Gives an error when the input is not a number
Answer

Note that the practice of TDD is the goal of the exercise, not the exact outcome.

Here is a possible solution:

def is_zero(x):
    """Determines if the input is one integer that is zero"""
    if not isinstance(x, int):
        raise TypeError("'x' must be of type int")
    if x == 0:
        return True
    return False

assert is_zero.__doc__
assert is_zero(0)
assert not is_zero(1)

has_thrown = False
try:
    is_zero("nonsense")
except TypeError:
    has_thrown = True
assert has_thrown
Need a video?

Here are two videos that show how to develop is_zero for Python and R:

Exercise 1: is_even

Learning objectives
  • Practice of TDD by developing a slightly more complicated function

Develop a function called is_even:

is_even
  • Function name: is_even
  • Output:
  • Returns True if the input is even
  • Returns False if the input is not even
  • ⚠ Gives an error when the input is not a number
Answer

Note that the practice of TDD is the goal of the exercise, not the exact outcome.

Here is a possible solution:

def is_even(x):
    if not isinstance(x, int):
        raise TypeError("'x' must be of type int")
    """Determine if the input is one integer that is even"""
    return x % 2 == 0

assert is_even.__doc__
assert is_even(2)
assert not is_even(1)

# 'is_even("nonsense")' throws a TypeError 
# because of the modulo operator

has_thrown = False
try:
    is_even(0.0)
except TypeError:
    has_thrown = True
assert has_thrown
Need a video?

Here are a videos that show how to develop is_even for Python:

Exercise 2: is_odd

Learning objectives
  • Practice of TDD by developing a slightly more complicated function

Develop a function called is_odd:

is_odd
  • Function name: is_odd
  • Output:
  • Returns True if the input is odd
  • Returns False if the input is not odd
  • ⚠ Gives an error when the input is not a number

Consider using the is_even function.

Answer

Note that the practice of TDD is the goal of the exercise, not the exact outcome.

Here is a possible solution:

def is_even(x):
    """Determine if the input is one integer that is even"""
    if not isinstance(x, int):
        raise TypeError("'x' must be of type int")
    return x % 2 == 0

assert is_even.__doc__
assert is_even(2)
assert not is_even(1)

# 'is_even("nonsense")' throws a TypeError 
# because of the modulo operator

has_thrown = False
try:
    is_even(0.0)
except TypeError:
    has_thrown = True
assert has_thrown

def is_odd(x):
    """Determine if the input is one integer that is odd"""
    return not is_even(x)

assert is_odd.__doc__
assert is_odd(1)

# Already passes, consider not putting it in
# assert not is_odd(2)

# Already passes, consider not putting it in
# has_thrown = False
# try:
#     is_odd(0.0)
# except TypeError:
#     has_thrown = True
# assert has_thrown
Need a video?

Here is a videos that show how to develop is_odd for Python:

Exercise 3: is_probability

Learning objectives
  • Practice of TDD by developing a slightly more complicated function

Develop a function called is_probability.

is_probability

  • Function name: is_probability
  • Output:
  • Returns True if the input is in the range [0.0, 1.0], that is from (and including) zero to (and including) one.
  • Returns False if the input is not a probability
  • ⚠ Gives an error when the input is not one number
Answer

Note that the practice of TDD is the goal of the exercise, not the exact outcome.

Here is a possible solution:

def is_probability(x):
    """Determine if `x` is a probability.

    Determine if `x` is a probability,
    i.e. a value between 0.0 and 1.0, including both 0.0 and 1.0.
    If `x` is not a floating point number, a `TypeError` is raised.

    Returns `True` if `x` is a probability
    """
    if not isinstance(x, float):
        msg = "'number' must be a floating point number. "
        raise TypeError(
            msg,
            "Actual type of 'number': ", type(x),
        )
    min_probability = 0.0
    max_probability = 1.0
    return x >= min_probability and x <= max_probability

Exercise 4: is_prime

Learning objectives
  • Practice of TDD by developing a slightly more complicated function

Develop a function called is_prime.

is_prime

  • Function name: is_prime
  • Output:
  • Returns True if the input is a prime number.
  • Returns False if the input is not a prime number.
  • ⚠ Gives an error when the input is not one number
Need a video?

Here is a videos that show how to develop is_prime for Python:

Exercise 5: practice

Learning objectives
  • Practice of TDD

Go to the Programming Formalisms page 'functions to practice TDD'. Pick a function at your level.

Technical rules

  • Use the shared GitHub repository for the learners of this course
  • Pick a branching model. When unsure, work on main
  • Use no testing framework or unittest. When unsure, use no testing frameworks
  • No testing framework: work in a file called learners/[your_names]/[function_name].py, e.g. learners/anna_and_bertil/is_zero.py
  • unittest framework: work in a file in the src folder (e.g. src/anna_and_bertil_utils.py), write the tests in the tests folder, e.g. tests/test_anna_and_bertil_utils.py

Social rules

  • Ping-Pong Pair programming
  • Discuss how and when to switch roles first
  • Person with first name first in alphabet starts
  • Try to be an exemplary duo

Signs you are doing great

In an online course:

  • A good pair has the driver sharing his/her screen
  • In a good pair, both people talk a lot
  • A good pair switches roles regularly
  • A good pair has a lot of commits

Conclusion

  • This session, we wrote unit tests
  • It is only those your boss may read
  • The literature assumes a responsible programmer writes tests, in C++ [Stroustrup & Sutter, 2017], R [Wickham, 2019] and Python [PEP 8]

Discussion

  • We only test manually
  • We only test on our own computer
  • We are not sure if our functions are tested completely
  • We do not test the code for style
  • We should consider using a testing framework

Videos

Title Length YouTube
TDD 1/3: TDD 5 mins YouTube
TDD 2/3: is_zero 16 mins YouTube
TDD 3/3: Introduce is_even 3 mins YouTube
Solution is_even 12 mins YouTube
TDD 2: introduce is_odd 7 mins YouTube
Solution is_odd 7 mins YouTube
TDD 3: TDD bottom line 4 mins YouTube
Solution is_prime 9 mins YouTube

References

  • [Alkaoud & Walcott, 2018] Alkaoud, Hessah, and Kristen R. Walcott. "Quality metrics of test suites in test-driven designed applications." International Journal of Software Engineering Applications (IJSEA) 2018 (2018).

  • [Beck, 2022] Beck, Kent. Test driven development: By example. Addison-Wesley Professional, 2022.
  • [Bhat & Nagappan, 2006] Bhat, Thirumalesh, and Nachiappan Nagappan. "Evaluating the efficacy of test-driven development: industrial case studies." Proceedings of the 2006 ACM/IEEE international symposium on Empirical software engineering. 2006.
  • [Erdogmus & Morisio, 2005] Erdogmus, Hakan, Maurizio Morisio, and Marco Torchiano. "On the effectiveness of the test-first approach to programming." IEEE Transactions on software Engineering 31.3 (2005): 226-237.
  • [George & Williams, 2004] George, Boby, and Laurie Williams. "A structured experiment of test-driven development." Information and software Technology 46.5 (2004): 337-342.
  • [Janzen & Saiedian, 2006] Janzen, David S., and Hossein Saiedian. "Test-driven learning: intrinsic integration of testing into the CS/SE curriculum." Acm Sigcse Bulletin 38.1 (2006): 254-258.

  • [Langr, 2013] Langr, Jeff. Better, Code, and Sleep Better. "Modern C++ Programming with Test-Driven Development." (2013).
  • [Madeyski et al., 2010] Madeyski, Lech, and Gestión de sistemas de información. Test-driven development: An empirical evaluation of agile practice. Heidelberg: Springer, 2010.

  • [Martin, 2011] Martin, Robert C. The clean coder: a code of conduct for professional programmers. Pearson Education, 2011.
  • [Mayr, 2005] Mayr, Herwig. Projekt Engineering: Ingenieurmäßige Softwareentwicklung in Projektgruppen. Hanser Verlag, 2005.
  • [Nagappan et al., 2008] Nagappan, Nachiappan, et al. "Realizing quality improvement through test driven development: results and experiences of four industrial teams." Empirical Software Engineering 13 (2008): 289-302.
  • [PEP 8] Van Rossum, Guido, Barry Warsaw, and Nick Coghlan. "PEP 8–style guide for python code." Python. org 1565 (2001): 28.
  • [Stroustrup & Sutter, 2017] Stroustrup, Bjarne, and Herb Sutter. "C++ Core Guidelines (2017)." URL http://isocpp. github. io/CppCoreGuidelines/CppCoreGuidelines.(Cited on pages 100 and 103) (2015).
  • [study I cannot find] in one of the classics, there was a bar chart that showed developers write functions such as is_prime with and without TDD and showed that TDD was twice as fast. TODO: find this reference

  • [Thomas & Hunt, 2019] Thomas, David, and Andrew Hunt. The Pragmatic Programmer: your journey to mastery. Addison-Wesley Professional, 2019.
  • [Wickham, 2019] Wickham, Hadley. Advanced R. Chapman and Hall/CRC, 2019.