--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tdd/lab-workbook.rst Tue Sep 07 20:09:50 2010 +0530
@@ -0,0 +1,146 @@
+======================================
+Lab Workbook - Test Driven Development
+======================================
+
+The notation that follows every question denotes the level on the
+Revised Bloom's Taxonomy.
+
+Lab - 1
+=======
+
+ 1. Write a stub function for calculating the LCM of two numbers.
+ - U-level
+ 2. Write the tests for the LCM function, place the tests in if
+ __name__ == '__main__': part of the Python file. Demonstrate that
+ the tests fail. - U-level
+ 3. Implement the code for the LCM function, using the gcd function
+ provided in the examples in the chapter. Demonstrate the tests
+ pass. (For the algorithm refer to Wikipedia - [0]) - Ap-level
+ 4. Alternatively, build a set of test cases, preferably a large
+ number of cases, place it in a text file and use these test cases
+ to test your LCM function. Demonstrate that tests still continue
+ to pass. - U-level
+
+[0] - http://en.wikipedia.org/wiki/Least_common_multiple#Reduction_by_the_greatest_common_divisor
+
+Lab - 2
+=======
+
+ 1. Write the stub function, followed by the tests(demonstrating the
+ failed tests), in turn followed by the code(demonstrating the
+ passing tests) to calculate the number of days between two
+ dates. Name your function num_of_days(). The function should take
+ two arguments, both being tuples. Each tuple represents the date
+ in the format of (dd, mm, yyyy) where dd, mm and yyyy are
+ integers. - Ap-level
+
+ 2. Rewrite the num_of_days() function to take the start date as an
+ optional argument. If the start date is not specified calculate
+ the number of days between the only specified date since Unix
+ epoch. Prior to manipulating the code to do this, make sure you
+ change the tests, make them fail and then refactor the code.
+ - Ap-level
+
+
+Lab -3
+======
+
+ 1. Move the tests that were written to GCD function in the examples
+ of this chapter to a separate function called test_gcd(). Do the
+ same for LCM function and num_of_days() function. Make sure when
+ the respective Python files are executed as stand alone scripts
+ these tests executed. - U-level
+ 2. Put all these files in a single directory called utils and run
+ the nosetests command. Make a report of the results. - U-level
+ 3. Write doctests to each of the above functions. Demonstrate and
+ report the results as executed by running the doctests using
+ doctest.testmod() function and using nosetests command. -Ap-level
+
+Lab - 4
+=======
+
+ 1. Consider the following use case: We are given a large list of
+ items called *data* where each item is a again a list with three
+ values: username, which is a string; status of the user which
+ can be one of the following three strings 'new', 'valid' or
+ 'invalid'; and the last login time which is a datetime Python
+ object. Write a function called **query** that takes a filter
+ dictionary as a parameter and returns the result of the items in
+ the *data* list. They keys of the dictionary can be 'user',
+ 'status' and 'logtime' and their corresponding values can be any
+ of the valid values for the corresponding key. Example filter
+ dictionary::
+
+ filter = {
+ 'user': 'john'
+ 'status': 'new'
+ }
+
+ Place your function in a file called query.py. Before writing the
+ actual function, follow the test driven development
+ approach. First write a stub, fail the tests and then write the
+ code and make sure the tests pass. Specifically use unittest
+ framework to test this function. Place your tests in a file
+ called test_query.py
+
+ A developer wrote a small utility function in a file named
+ user_utils.py which uses your **query** function which looks as
+ follows::
+
+ def login_util(user=None):
+ """Takes a user name and returns his last login time if the
+ user is a valid user, else return None. If the user is
+ 'host' it returns the last login time of all the users.
+ """
+
+ filter_dict = {
+ 'user': user
+ 'status': 'active'
+ }
+
+ if user == 'host':
+ filter_dict['status'] + ['new', 'invalid']
+
+ return query(filter_dict)
+
+ Unfortunately the developer did not provide us with the test
+ cases. We wrote the following test cases for you to only discover
+ that the function miserably fails.
+
+ The tests were placed in a file called test_user_utils.py and we
+ have used the unittest framework::
+
+ import query
+ import user_utils
+ import unittest
+
+ class TestUserUtils(unittest.TestCase):
+
+ def setUp(self):
+ """Boiler plate method to provide common data to all
+ the test methods.
+ """
+ self.test_names = ['Alex', 'Guido', 'Thomas', 'host',
+ 'Tom', 'James']
+ self.data_len = len(query.data)
+
+ def test_login_utils(self):
+ """Tests for the login_utils function.
+ """
+
+ for name in self.test_names:
+ if name == 'host':
+ assertEqual(len(user_utils.login_utils(name)), self.data_len)
+ else:
+ assertLess(len(user_utils.login_utils(name)), self.data_len)
+
+ def tearDown(self):
+ """Boiler plate method to clean up all the data created
+ for tests.
+ """
+
+ del self.test_names
+ del self.data_len
+
+ Fix the bug, run the tests to make sure the function passes the
+ tests and if possible refactor the code with a better approach. - An-level
--- a/tdd/tdd.rst Tue Sep 07 20:04:28 2010 +0530
+++ b/tdd/tdd.rst Tue Sep 07 20:09:50 2010 +0530
@@ -525,13 +525,18 @@
if __name__ == '__main__':
unittest.main()
-Since we don't want to read this file into memory each time we run a
-separate test method, we will read all the data in the file into
-Python lists in the setUp method. The entire data file is kept in a
-list called test_cases which happens to be an attribute of the
-TestGCDFunction class. In the tearDown method of the class we
-will delete this attribute to free up the memory and close the
-opened file.
+Please note that although we highly recommend to write a docstring for
+all the classes, functions and modules we have not done so to keep
+above code compact and we have left it as an exercise for the you to
+add them.
+
+Coming back to tests themselves, since we don't want to read this file
+into memory each time we run a separate test method, we will read all
+the data in the file into Python lists in the setUp method. The entire
+data file is kept in a list called test_cases which happens to be an
+attribute of the TestGCDFunction class. In the tearDown method of the
+class we will delete this attribute to free up the memory and close
+the opened file.
Our actual test code sits in the method which begins with the name
**test_** as said earlier, the test_gcd method. Note that we import