tdd/tdd.rst
author Puneeth Chaganti <punchagan@fossee.in>
Mon, 31 Jan 2011 12:24:43 +0530
changeset 149 4499aebbee83
parent 131 8888712bed39
permissions -rw-r--r--
vcs: Fix pygments highlighting of code blocks with $ and ' Pygments highlighting breaks when a code block ends with a lone $ on a line or when it has an unmatched '.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
116
958e2e9f2858 Adding the page header. Better late than never.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 115
diff changeset
     1
=======================
958e2e9f2858 Adding the page header. Better late than never.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 115
diff changeset
     2
Test Driven Development
958e2e9f2858 Adding the page header. Better late than never.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 115
diff changeset
     3
=======================
958e2e9f2858 Adding the page header. Better late than never.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 115
diff changeset
     4
89
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
     5
Fundamentals
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
     6
============
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
     7
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
     8
Test Driven Development, abbreviated as TDD is a method of software
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
     9
development which banks on the idea of writing test cases that fail for the
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    10
code that doesn't even exist yet. The actual code is written later to pass
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    11
the test and then refactored.
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    12
109
0afd25eadf41 Add a section on real life tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 89
diff changeset
    13
First "Test"
0afd25eadf41 Add a section on real life tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 89
diff changeset
    14
============
89
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    15
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    16
Writing a test is simple. Writing a failing test? It is much more simple.
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    17
Let us consider a very simple program which returns the Greatest Common
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    18
Divisor (GCD) of two numbers. Since the test cases for the code is written
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    19
prior to the code itself, it is necessary to have a clear idea of the code
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    20
units that our program will contain. Let us attempt to clearly define the
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    21
code units in our case of a GCD program. Let our program contain one and
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    22
only one function called gcd() which takes in two arguments as parameters.
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    23
These arguments are the numbers for which GCD must be computed. The gcd()
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    24
function returns a single value which is the GCD of the two arguments
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    25
passed. So if we want to find out GCD of 44, 23, I will call my code unit
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    26
as c = gcd(44, 23) where c will contain the GCD of those two numbers.
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    27
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    28
Now we have defined our code units, how will we write tests? Before writing
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    29
the test, a very fundamental question arises in our minds. How do tests
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    30
look like? So let us answer this question first. Tests are nothing but a
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    31
series of assertions which are either True or False depending on the
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    32
expected behaviour of the code. We tell our tests whether our code unit
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    33
asserts True or asserts False based on the expected behaviour of the code
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    34
units. If we happen to run the tests now we are sure to get errors. Oh! But
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    35
why? We don't even have the function gcd to call. The test code doesn't
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    36
even compile! So what should we do now? So the idea is to first write the
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    37
stubs for the code units before we start writing tests. This is necessary
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    38
for two reasons. Firstly, by writing the stubs for the code units we will
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    39
be able to correctly decide and fix on to the code units that we have
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    40
planned to include in our program. We have a clear cut idea as to how our
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    41
program is structured, how the tests must be written among other
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    42
things. Secondly, the tests must at least compile and then fail! If the
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    43
tests don't even compile, that doesn't mean the tests failed. It means
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    44
it was a failure on the programmer's part. Let us define our stub::
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    45
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    46
  def gcd(a, b):
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    47
      pass
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    48
110
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    49
This stub does nothing other than defining a new function called gcd
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    50
which takes two parameters a and b for which the GCD must be
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    51
calculated. The body of the function just contains Python's **pass**
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    52
statement which means it does nothing, i.e. empty. We have our stub
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    53
ready. One important thing we need to keep in mind when we adopt TDD
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    54
methodology is that we need to have a clear set of results defined for
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    55
our code units. To put it more clearly, for every given set of inputs
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    56
as test case we must have, before hand, the exact outputs that are
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    57
expected for those input test cases. If we don't have that we have
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    58
failed in the first step of the TDD methodology itself. We must never
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    59
run looking for outputs for our test cases after we have the code
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    60
ready or even while writing tests. The expected outputs/behaviour must
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    61
be in our hands before we start writing tests. Therefore let us define
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    62
our test cases and the expected output for those inputs. Let one of
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    63
our test cases be 48 and 64 as *a* and *b* respectively. For this test
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    64
case we know that the GCD is 16. So that is the expected output. Let
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    65
our second test case be 44 and 19 as *a* and *b* respectively. We know
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    66
that their GCD is 1 by simple paper and pen calculation.
89
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    67
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    68
Now we know what a test is? What are the ingredients required to write
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    69
tests? So what else should we wait for? Let us write our first test!::
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    70
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    71
  tc1 = gcd(48, 64)
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    72
  if tc1 != 16:
110
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    73
      print "Test failed for the case a=48 and b=64. Expected 16. Obtained %d instead." % tc1
89
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    74
      exit(1)
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    75
  
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    76
  tc2 = gcd(44, 19)
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    77
  if tc2 != 1:
110
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    78
      print "Test failed for the case a=44 and b=19. Expected 1. Obtained %d instead." % tc2
89
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    79
      exit(1)
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    80
449760bc4089 Adding initial fundamentals and basic test content for TDD module.
Madhusudan.C.S <madhusudancs@gmail.com>
parents:
diff changeset
    81
  print "All tests passed!"
109
0afd25eadf41 Add a section on real life tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 89
diff changeset
    82
110
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    83
Let us put all these in a file and call this file **gcd.py**::
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    84
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    85
  def gcd(a, b):
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    86
      pass
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    87
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    88
  if __name__ == '__main__':
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    89
      tc1 = gcd(48, 64)
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    90
      if tc1 != 16:
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    91
          print "Test failed for the case a=48 and b=64. Expected 16. Obtained %d instead." % tc1
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    92
          exit(1)
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    93
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    94
      tc2 = gcd(44, 19)
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    95
      if tc2 != 1:
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    96
          print "Test failed for the case a=44 and b=19. Expected 1. Obtained %d instead." % tc2
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    97
          exit(1)
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    98
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
    99
      print "All tests passed!"
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   100
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   101
Note that we have introduced a new semantic which uses two new magic names
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   102
in Python *__name__* and *__main__*. This is a very common idiom used in
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   103
Python. Every Python code in a file can be run in two ways: Either as an
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   104
independent stand-alone script or as a Python module which can be imported
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   105
by other Python scripts or modules. When the idiom::
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   106
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   107
  if __name__ == '__main__':
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   108
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   109
is used, the code within this if block is executed first when we run the
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   110
Python file as a stand-alone script. In other words, when we run this
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   111
python file as a stand-alone script the control of the program first starts
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   112
from the code that is within this if block from which the control is
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   113
transferred to other parts of the program or to other modules from
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   114
here. This comes as an extremely handy feature especially when we want to
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   115
test our modules individually. Now let us run our code as a stand-alone
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   116
script.::
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   117
120
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   118
  $ python gcd.py
110
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   119
  Traceback (most recent call last):
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   120
    File "gcd.py", line 7, in <module> print "Test failed for the case a=48 and b=64. Expected 16. Obtained %d instead." % tc1
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   121
  TypeError: %d format: a number is required, not NoneType
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   122
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   123
Now we have our tests, the test cases and the code unit stub at
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   124
hand. We also have the failing test. So we know for sure that we have
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   125
cleared the first check point of TDD where the tests have failed. The
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   126
failing tests also give a green signal for us to go ahead to our next
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   127
check point i.e. to write the actual code in our code unit and make
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   128
the test pass. So let us write the code for the gcd function by
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   129
removing the **pass** control statement which had just created a gcd
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   130
function stub for us.
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   131
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   132
Most of us have learnt in high school math classes that the best and
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   133
the easiest known algorithm to compute the gcd of two numbers was
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   134
given to us 2300 years ago by a greek mathematician named Euclid. So
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   135
let us use the Euclid's algorithm to compute the gcd of two numbers a
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   136
and b::
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   137
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   138
  def gcd(a, b):
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   139
      if a == 0:
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   140
          return b
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   141
      while b != 0:
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   142
          if a > b:
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   143
              a = a - b
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   144
          else:
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   145
              b = b - a
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   146
      return a
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   147
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   148
**Note**: If you are unaware of Euclidean algorithm to compute the gcd
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   149
of two numbers please refer to it on wikipedia. It has a very detailed
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   150
explanation of the algorithm and its proof of validity among other
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   151
things.
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   152
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   153
Now let us run our script which already has the tests written in it
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   154
and see what happens::
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   155
120
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   156
  $ python gcd.py
110
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   157
  All tests passed!
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   158
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   159
Success! We managed to pass all the tests. But wasn't that code simple
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   160
enough? Indeed it was. If you take a closer look at the code you will
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   161
soon realize that the chain of subtraction operations can be replaced
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   162
by a modulo operation i.e. taking remainders of the division between
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   163
the two numbers since they are equivalent operations. Also modulo
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   164
operation is far better than chain of subtractions because you will
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   165
reduce much faster using modulo operation than the subtraction. For
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   166
example if let us take 25, 5 as a and b in our example. If we write
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   167
down the steps of the algorithm written above we have the following:
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   168
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   169
Step 1: a = 25 b = 5: Since both a and b are not 0 and b is greater
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   170
than a: b = 25 - 5 = 20
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   171
Step 2: Since b is still not 0 and b is greater than a: b = 20 - 5 =
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   172
15
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   173
Step 3: Since b is still not 0 and b is greater than a: b = 15 - 5 =
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   174
10
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   175
Step 4: Since b is still not 0 and b is greater than a: b = 10 - 5 = 5
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   176
Step 5: Since b is still not 0 and b is equal to a: b = 5 - 5 = 0
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   177
Step 6: Since b is 0 the gcd is a = 5 which is returned
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   178
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   179
If we adopt the modulo operation instead of subtraction and follow the
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   180
steps:
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   181
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   182
Step 1: a = 25 b = 5: Since both a and b are not 0 and b is greater
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   183
than a: b = 25 % 5 = 0
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   184
Step 2: Since b is 0 the gcd is a = 5 which is returned
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   185
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   186
Wow! That was overwhelmingly lesser number of steps! So now we are
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   187
convinced that if we replace the subtraction operation with the modulo
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   188
operation our code performs much better. But if we think carefully we
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   189
know that the modulo of a and b is less than b irrespective of how
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   190
large the value of a is, including the case where a is already less
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   191
than b. So we can eliminate that extra conditional **if** statement by
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   192
just swapping the result of the modulo operation to the position of b
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   193
and b to the position of a. This ensures that a is always greater than
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   194
b and if not the swapping combined with modulo operation takes care of
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   195
it. To exemplify it, if a = 5 and b = 25 then by swapping and
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   196
performing modulo we have a = b = 25 and b = a % b = 5 % 25 = 5 and
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   197
hence we proceed. So let us replace our original code with this new
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   198
improved code we have come up with simple observations::
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   199
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   200
  def gcd(a, b):
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   201
      while b != 0:
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   202
          a, b = b, a % b
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   203
      return a
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   204
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   205
Executing our script again we will see that all the tests pass. One
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   206
final improvement we can think of which is not necessary in terms of
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   207
efficiency but is certainly good to do keeping in mind the readability
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   208
is that we can use the concept of recursion for the same
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   209
algorithm. Without going into much detail this is how the code looks
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   210
if we use a recursive approach::
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   211
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   212
  def gcd(a, b):
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   213
      if b == 0:
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   214
          return a
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   215
      return gcd(b, a%b)
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   216
117
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   217
Much shorter and sweeter! And it passes all the tests! But there is
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   218
one small problem yet. For the users of this function there is no way
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   219
to determine how to use it, how many parameters it takes what it
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   220
returns among other things. And same as well for those who read the
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   221
code. So this function is not a very well written piece of code since
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   222
it lacks documentation. So to make this function mode readable let us
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   223
add the docstring for this function. Rewriting the function with the
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   224
docstring looks like this::
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   225
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   226
  def gcd(a, b):
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   227
      """Returns the Greatest Common Divisor of the two integers
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   228
      passed as arguments.
110
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   229
117
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   230
      Args:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   231
        a: an integer
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   232
        b: another integer
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   233
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   234
      Returns: Greatest Common Divisor of a and b
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   235
      """
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   236
      if b == 0:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   237
          return a
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   238
      return gcd(b, a%b)
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   239
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   240
Now we have refactored our code enough to make it well written piece
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   241
of code. Let us move on.
110
4e7b98636b58 Improved the initial Introduction on tests content.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 109
diff changeset
   242
109
0afd25eadf41 Add a section on real life tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 89
diff changeset
   243
More realistic "Tests"
0afd25eadf41 Add a section on real life tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 89
diff changeset
   244
======================
0afd25eadf41 Add a section on real life tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 89
diff changeset
   245
112
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   246
Now we have successfully completed writing our first test, writing the
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   247
relevant code and ensured the tests passed. We also refactored our
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   248
code to perform better. With the knowledge of all these and some
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   249
concepts and semantics like __main__ magic names we learnt we have
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   250
come a long way with respect to writing tests. But our thirst is still
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   251
unquenched! We want to do more and more tests! Not just write better
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   252
code but also better tests! So let us keep building upon what we have
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   253
learnt so far.
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   254
115
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   255
Let us start writing tests for more realistic test cases. Generally
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   256
tests are predetermined as said above, if not the software design in
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   257
itself is flawed. The predetermined tests are stored along with the
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   258
test code in some persistent format like in a database, a text file, a
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   259
file of specific format like XML or in some other way. Let us continue
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   260
with our example of GCD function. We will keep all our test cases in a
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   261
text file, which is indeed persistent. Let us specify the format of
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   262
the test data in our file as follows.
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   263
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   264
  1. The file has multiple lines of test data.
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   265
  2. Each line in this file corresponds to a single test case.
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   266
  3. Each line consists of three comma separated coloumns:
112
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   267
115
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   268
     i. First two coloumns are the integers for which the GCD has to
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   269
        be computed
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   270
     ii. Third coloumn is the expected GCD to the preceding two
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   271
         numbers.
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   272
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   273
So how do we write our tests to use these test cases? Pretty simple, let
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   274
us review the machinery required first.
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   275
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   276
  1. File reading: We already have learnt this in the modules on
112
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   277
     Basic Python.
115
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   278
  2. Parsing the read data from the file: This just involves a using a
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   279
     **for** loop which iterates over the data line by line since we
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   280
     know that the file contains each test case as a sepate line which
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   281
     are equivalent to the file records and hence parse the data line
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   282
     by line as strings as we iterate over it and convert it to the
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   283
     required data type.
112
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   284
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   285
Since we already have all the machinery required, let us proceed writing
115
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   286
our test cases. We do not need not make any changes to the gcd
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   287
function so we will just write down the test here. Let us call our
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   288
data file gcd_testcases.dat::
112
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   289
115
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   290
  if __name__ == '__main__':
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   291
      for line in open('gcd_testcases.dat'):
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   292
          values = line.split(', ')
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   293
          a = int(values[0])
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   294
          b = int(values[1])
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   295
          g = int(values[2])
112
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   296
115
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   297
          tc = gcd(a, b)
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   298
          if tc != g:
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   299
              print "Test failed for the case a=%d and b=%d. Expected %d. Obtained %d instead." % (a, b, g, tc)
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   300
              exit(1)
112
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   301
115
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   302
      print "All tests passed!"
112
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   303
115
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   304
When we execute the gcd.py script again we will notice that all the
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   305
tests passed.
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   306
117
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   307
Python Testing Framework
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   308
========================
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   309
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   310
Python provides two ways to test the code we have written. One of them
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   311
is the unittest framework and the the other is the doctest module.
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   312
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   313
doctest
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   314
~~~~~~~
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   315
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   316
To start with let us discuss the doctest module. As we have already
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   317
discussed a well written piece of code must always be accompanied by
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   318
its documentation. For a function or a module we document them in their
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   319
respective docstrings. In addition to this, we can also place the
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   320
samples of using these functions or modules in the Python interactive
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   321
interpreter in the docstrings. When we run the doctest module it picks
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   322
up all such interactive session samples, executes them and determines
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   323
if the documented piece of code runs as it is documented. Let us see
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   324
how to write doctests for our gcd function::
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   325
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   326
  def gcd(a, b):
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   327
      """Returns the Greatest Common Divisor of the two integers
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   328
      passed as arguments.
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   329
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   330
      Args:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   331
        a: an integer
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   332
        b: another integer
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   333
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   334
      Returns: Greatest Common Divisor of a and b
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   335
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   336
      >>> gcd(48, 64)
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   337
      16
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   338
      >>> gcd(44, 19)
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   339
      1
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   340
      """
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   341
      if b == 0:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   342
          return a
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   343
      return gcd(b, a%b)
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   344
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   345
This is all a doctest is. To explain it in more simple terms tests
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   346
which are written as part of the docstrings are called as
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   347
doctests. Now how do we use our doctest module to execute this
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   348
tests. That is fairly straight forward as well. All we need to do is
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   349
tell the doctest module to execute. Let us place this piece of code at
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   350
the same place where we placed our tests earlier. So putting all these
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   351
together we have our gcd.py module which looks as follows::
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   352
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   353
  def gcd(a, b):
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   354
      """Returns the Greatest Common Divisor of the two integers
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   355
      passed as arguments.
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   356
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   357
      Args:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   358
        a: an integer
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   359
        b: another integer
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   360
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   361
      Returns: Greatest Common Divisor of a and b
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   362
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   363
      >>> gcd(48, 64)
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   364
      16
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   365
      >>> gcd(44, 19)
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   366
      1
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   367
      """
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   368
      if b == 0:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   369
          return a
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   370
      return gcd(b, a%b)
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   371
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   372
  if __name__ == "__main__":
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   373
      import doctest
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   374
      doctest.testmod()
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   375
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   376
All we need to do is import the doctest module that is part of the
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   377
Python's standard library. Call the testmod() function in this
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   378
module. This function automatically checks for all the docstrings that
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   379
have sample sessions from the interactive interpreter, if they exist
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   380
it executes them and compares the output with the results as specified
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   381
in the sample sessions. It complains if the results don't match as
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   382
documented. When we execute this script as a stand-alone script we
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   383
will get back the prompt with no messages which means all the tests
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   384
passed::
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   385
120
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   386
  $ python gcd.py
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   387
  $ 
117
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   388
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   389
If we further want to get a more detailed report of the tests that
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   390
were executed we can run python with -v as the command line option
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   391
to the script::
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   392
120
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   393
  $ python gcd.py -v
117
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   394
  Trying:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   395
      gcd(48, 64)
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   396
  Expecting:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   397
    16
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   398
  ok
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   399
  Trying:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   400
      gcd(44, 19)
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   401
  Expecting:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   402
      1
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   403
  ok
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   404
  1 items had no tests:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   405
      __main__
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   406
  1 items passed all tests:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   407
     2 tests in __main__.gcd
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   408
  2 tests in 2 items.
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   409
  2 passed and 0 failed.
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   410
  Test passed.
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   411
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   412
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   413
**Note:** We can have the sample sessions as test cases as long as the
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   414
outputs of the test cases do not contain any blank lines. In such
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   415
cases we may have to use the exact string *<BLANKLINE>*
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   416
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   417
For the sake of illustrating a failing test case, let us assume that
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   418
we made a small mistake in our code. Instead of returning **a** when b
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   419
= 0 we typed it as return b when b = 0. So all the gcds returned will
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   420
have the value of 0 in such a piece of code. The code looks as
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   421
follows::
112
0b01bb6ea6b8 Add a work-in progress section on writing realistic tests.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 110
diff changeset
   422
117
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   423
  def gcd(a, b):
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   424
      """Returns the Greatest Common Divisor of the two integers
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   425
      passed as arguments.
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   426
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   427
      Args:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   428
        a: an integer
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   429
        b: another integer
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   430
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   431
      Returns: Greatest Common Divisor of a and b
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   432
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   433
      >>> gcd(48, 64)
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   434
      16
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   435
      >>> gcd(44, 19)
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   436
      1
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   437
      """
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   438
      if b == 0:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   439
          return a
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   440
      return gcd(b, a%b)
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   441
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   442
Executing this code snippet without -v option to the script::
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   443
120
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   444
  $ python gcd.py
117
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   445
  **********************************************************************
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   446
  File "gcd.py", line 11, in __main__.gcd
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   447
  Failed example:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   448
      gcd(48, 64)
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   449
  Expected:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   450
      16
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   451
  Got:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   452
      0
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   453
  **********************************************************************
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   454
  File "gcd.py", line 13, in __main__.gcd
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   455
  Failed example:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   456
      gcd(44, 19)
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   457
  Expected:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   458
      1
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   459
  Got:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   460
      0
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   461
  **********************************************************************
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   462
  1 items had failures:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   463
     2 of   2 in __main__.gcd
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   464
  ***Test Failed*** 2 failures.
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   465
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   466
The output clearly complains that there were exactly two test cases
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   467
that failed. If we want a more verbose report we can pass -v option to
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   468
the script. This is pretty much about the doctest module in
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   469
Python. doctest is extremely useful when we want to test each Python
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   470
function or module individually. For more information about the
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   471
doctest module refer to the Python library reference on doctest[0].
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   472
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   473
unittest framework
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   474
~~~~~~~~~~~~~~~~~~
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   475
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   476
Not too far ahead we go we, we will start complaining that the doctest
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   477
is not sufficient to write complicated tests especially when we want
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   478
to automate our tests, write tests that need to test for more
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   479
convoluted code pieces. For such scenarios Python provides a unittest
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   480
framework.  unittest framework provides methods to efficiently
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   481
automate tests, setup and teardown functionalities which helps to
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   482
setup the initializing code and data for executing the specific tests
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   483
and cleanly shutting them down once the tests are executed and ways to
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   484
aggregate tests into collections and better way of reporting the
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   485
tests.
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   486
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   487
Let us continue testing our gcd function in the Python module named
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   488
gcd.py. To get ourselves started, the unittest framework expects us to
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   489
subclass TestCase class in unittest module and place all our test code
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   490
as methods of this class. We will begin the name of the test method
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   491
with **test_** so that the test runner knows which methods are to be
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   492
executed as tests. We will use the test cases supplied by
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   493
gcd_testcases.dat. Lastly, to illustrate the way to test Python code
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   494
as a module let create a new file called test_gcd.py following the
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   495
same convention used to name the test methods. We will place our test
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   496
code within test_gcd.py module. Our test code looks like this::
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   497
  
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   498
  import gcd
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   499
  import unittest
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   500
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   501
  class TestGcdFunction(unittest.TestCase):
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   502
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   503
      def setUp(self):
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   504
          self.test_file = open('gcd_testcases.dat')
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   505
          self.test_cases = []
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   506
          for line in self.test_file:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   507
              values = line.split(', ')
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   508
              a = int(values[0])
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   509
              b = int(values[1])
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   510
              g = int(values[2])
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   511
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   512
              self.test_cases.append([a, b, g])
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   513
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   514
      def test_gcd(self):
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   515
          for case in self.test_cases:
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   516
              a = case[0]
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   517
              b = case[1]
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   518
              g = case[2]
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   519
              self.assertEqual(gcd.gcd(a, b), g)
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   520
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   521
      def tearDown(self):
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   522
          self.test_file.close()
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   523
          del self.test_cases
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   524
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   525
  if __name__ == '__main__':
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   526
      unittest.main()
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   527
131
8888712bed39 Added a paragraph about docstrings in the chapter.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 120
diff changeset
   528
Please note that although we highly recommend to write a docstring for
8888712bed39 Added a paragraph about docstrings in the chapter.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 120
diff changeset
   529
all the classes, functions and modules we have not done so to keep
8888712bed39 Added a paragraph about docstrings in the chapter.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 120
diff changeset
   530
above code compact and we have left it as an exercise for the you to
8888712bed39 Added a paragraph about docstrings in the chapter.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 120
diff changeset
   531
add them.
8888712bed39 Added a paragraph about docstrings in the chapter.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 120
diff changeset
   532
8888712bed39 Added a paragraph about docstrings in the chapter.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 120
diff changeset
   533
Coming back to tests themselves, since we don't want to read this file
8888712bed39 Added a paragraph about docstrings in the chapter.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 120
diff changeset
   534
into memory each time we run a separate test method, we will read all
8888712bed39 Added a paragraph about docstrings in the chapter.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 120
diff changeset
   535
the data in the file into Python lists in the setUp method. The entire
8888712bed39 Added a paragraph about docstrings in the chapter.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 120
diff changeset
   536
data file is kept in a list called test_cases which happens to be an
8888712bed39 Added a paragraph about docstrings in the chapter.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 120
diff changeset
   537
attribute of the TestGCDFunction class. In the tearDown method of the
8888712bed39 Added a paragraph about docstrings in the chapter.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 120
diff changeset
   538
class we will delete this attribute to free up the memory and close
8888712bed39 Added a paragraph about docstrings in the chapter.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 120
diff changeset
   539
the opened file.
117
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   540
120
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   541
Our actual test code sits in the method which begins with the name
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   542
**test_** as said earlier, the test_gcd method. Note that we import
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   543
the gcd Python module we have written at the top of this test file and
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   544
from this test method we call the gcd function within the gcd module
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   545
to be tested with the each set of **a** and **b** values in the
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   546
attribute test_cases. Once we execute the function we obtain the
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   547
result and compare it with the expected result as stored in the
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   548
corresponding test_cases attribute using the assertEqual methods
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   549
provided by our parent class TestCase in the unittest framework. There
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   550
are several other assertion methods supplied by the unittest
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   551
framework. For a more detailed information about this, refer to the
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   552
unittest library reference at [1].
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   553
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   554
nose
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   555
====
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   556
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   557
Now we know almost all the varities of tests we may have to use to
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   558
write self-sustained, automated tests for our code. There is one last
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   559
thing that is left. However one question remains, how do we easily
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   560
organize choose and run the tests that is scattered around several
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   561
files? 
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   562
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   563
To further explain, the idea of placing tests with in the Python
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   564
scripts and executing that test scripts themselves as stand-alone
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   565
scripts works well as long as we have our code in a single Python file
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   566
or as long as the tests for each script can be run separately. But in
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   567
a more realistic software development scenario, often this is not the
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   568
case. The code is spread around multiple Python modules and may be
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   569
even across several Python packages.
115
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   570
120
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   571
In such a such a scenario we wish we had a better tool to
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   572
automatically aggregate these tests and execute them. Fortunately for
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   573
us there exists a tool called nose. Although nose is not part of the
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   574
standard Python distribution itself, it can be very easily installed
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   575
by using easy_install command as follows::
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   576
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   577
  $ easy_install nose
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   578
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   579
Or download the nose package from [2], extracting the archive and
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   580
running the command from the extracted directory::
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   581
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   582
  $ python setup.py install
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   583
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   584
Now we have nose up and running, but how do we use it? It is very
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   585
straight forward as well. We will use the command provided by nose
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   586
called as nosetests. Run the following command in the top level
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   587
directory of your code::
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   588
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   589
  $ nosetests
115
7455326cac40 Completed the section on realistic test scenarios and introductory paragraphs on nose.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 112
diff changeset
   590
120
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   591
Thats all, nose automatically picks all the tests in all the
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   592
directories and subdirectories in our code base and executes them
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   593
all. However if we want to execute specific tests we can pass the test
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   594
file names or the directories as arguments to nosetests command. For a
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   595
detailed explanation about this, refer to [3]
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   596
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   597
Conclusion
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   598
==========
117
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   599
120
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   600
Now we have all the trappings we want to write state-of-the art
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   601
tests. To emphasize the same point again, any code which was written
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   602
before writing the test and the testcases in hand is flawed by
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   603
design. So it is recommended to follow the three step approach while
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   604
writing code for any project as below:
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   605
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   606
  1. Write failing tests with testcases in hand.
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   607
  2. Write the code to pass the tests.
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   608
  3. Refactor the code for better performance.
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   609
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   610
This approach is very famously known to the software development world
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   611
as "Red-Green-Refactor" approach[4].
117
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   612
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   613
fab0281a992f Add materials on doctest and unittest framework.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 116
diff changeset
   614
[0] - http://docs.python.org/library/doctest.html
120
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   615
[1] - http://docs.python.org/library/unittest.html
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   616
[2] - http://pypi.python.org/pypi/nose/
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   617
[3] - http://somethingaboutorange.com/mrl/projects/nose/0.11.2/usage.html
7428e411bd7a Final portions of Test Driven Development.
Madhusudan.C.S <madhusudancs@gmail.com>
parents: 117
diff changeset
   618
[4] - http://en.wikipedia.org/wiki/Test-driven_development