Unit Testing
&
TDD

Different kinds of tests

Why do we write tests?

  • They let us know if our code is correct
  • They provide us with an easy instrument to check our program
  • Easier refactoring and maintainability
  • More reliable software
  • What else?

Unit testing in Python (1)

import unittest
from word_counter import get_word_occurences


class TestGetWordOccurences(unittest.TestCase):
    def test_returns_zero_if_word_not_in_text(self):
        word = 'marto'
        text = 'someone is testing'

        result = get_word_occurences(word, text)

        self.assertEqual(result, 0)

    def test_returns_one_if_word_is_exactly_once_in_text(self):
        word = 'marto'
        text = 'marto is testing'

        result = get_word_occurences(word, text)

        self.assertEqual(result, 1)


if __name__ == '__main__':
    unittest.main()

(A)rrange

(A)ct

(A)ssert

(A)rrange

(A)ct

(A)ssert

Unit testing in Python (2)

  1.  Import unittest
  2.  Import the methods that you will test
  3.  Create new test case class: SomeClass(unittest.TestCase)
  4.  Define test methods in the test class
  5.  Use self.assertEqual(), self.assertFalse(), etc.
  6.  Don't forget to call unittest.main()
if __name__ == '__main__':
	unittest.main()

Running Tests

python week01/01.PythonProblems/test_word_counter.py                                                               
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
watch python3 week01/01.PythonProblems/test_word_counter.py

Every 2,0s: python3 week01/01.PythonProblems/test_word_counter.py            martin056-lenovo: Wed Mar  4 15:05:38 2020

..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

Using watch

Running tests by hand

Rules/Best Practices (1)

Each method should do exactly one thing.

Each test method should test exactly one unit/thing.

Rules/Best Practices (2)

  • The tests must be independent
  • Test names and methods must be self-explaining
  • Cover all parts of your program with tests
  • Add tests for the edge-cases
  • Don't add unnecessary* tests

Test-Driven Development

1

2

3

Add a failing test

import unittest


class TestGetWordOccurences(unittest.TestCase):
    def test_returns_all_word_occurances_in_text(self):
        word = 'marto'
        text = 'marto is testing'

        result = get_word_occurences(word, text)

        self.assertEqual(result, 1)
E
======================================================================
ERROR: test_returns_zero_if_word_not_in_text (__main__.TestGetWordOccurences)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "week01/01.PythonProblems/test_word_counter.py", line 9, in test_returns_zero_if_word_not_in_text
    result = get_word_occurences(word, text)
NameError: name 'get_word_occurences' is not defined

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

1

3

2

Make test succeed

def get_word_occurences(word, text):
    return int(word in text)


class TestGetWordOccurences(unittest.TestCase):
    def test_returns_all_word_occurances_in_text(self):
        word = 'marto'
        text = 'marto is testing'

        result = get_word_occurences(word, text)

        self.assertEqual(result, 1)
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

1

3

2

Refactor

def get_word_occurences(word, text):
    return int(word in text)


class TestGetWordOccurences(unittest.TestCase):
    def test_returns_all_word_occurances_in_text(self):
        word = 'marto'
        text = 'marto is testing'

        result1 = get_word_occurences(word, text)
        result2 = get_word_occurences(word, f'{text} marto marto')

        self.assertEqual(result1, 1)
        self.assertEqual(result2, 3)
FAIL: test_returns_all_word_occurances_in_text (__main__.TestGetWordOccurences)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "week01/01.PythonProblems/test_word_counter.py", line 26, in test_returns_all_word_occurances_in_text
    self.assertEqual(result2, 3)
AssertionError: 1 != 3

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (failures=1)

1

3

2

Repeat!

1

3

2

  • Run tests at each iteration!
  • Don't change working functionality.
  • Improve code and test quality.
  • Don't repeat yourself!
  • Stop when you have all of the functionallity in place.

Benefits

  • Better interface
  • Better code design
  • Larger code coverage
  • Almost removes the debugging process since we work on really small iterations
  • Ensures we have all of the desired functionallity

Questions?

Python 101 9th Unit Testing & TDD

By Hack Bulgaria

Python 101 9th Unit Testing & TDD

  • 1,001