diff options
author | hardythe1 | 2014-06-13 18:29:04 +0530 |
---|---|---|
committer | hardythe1 | 2014-06-13 18:29:04 +0530 |
commit | 2f9d25d3e6d8ecaed6f86452c845fd9c70154710 (patch) | |
tree | 039e33eeef6f719078dd34722be6ea7a66e5b78b /lecture_notes/test_driven_development | |
parent | a6b748ea000473c4cb36ec769a64fa5c9595a59e (diff) | |
download | sees-2f9d25d3e6d8ecaed6f86452c845fd9c70154710.tar.gz sees-2f9d25d3e6d8ecaed6f86452c845fd9c70154710.tar.bz2 sees-2f9d25d3e6d8ecaed6f86452c845fd9c70154710.zip |
changes in the directory so that it can be compiled by sphinx
Diffstat (limited to 'lecture_notes/test_driven_development')
6 files changed, 0 insertions, 939 deletions
diff --git a/lecture_notes/test_driven_development/generate_testcases.py b/lecture_notes/test_driven_development/generate_testcases.py deleted file mode 100644 index 17b48a7..0000000 --- a/lecture_notes/test_driven_development/generate_testcases.py +++ /dev/null @@ -1,36 +0,0 @@ -import random - -def gcd(a, b): - while b != 0: - a, b = b, a % b - return a - -a = random.sample(xrange(100), 10) -b = random.sample(xrange(1000), 10) -c = random.sample(xrange(10000), 10) -c = random.sample(xrange(10000), 10) -d = random.sample(xrange(100000), 10) -e = random.sample(xrange(1000000), 10) -f = a + b + c + d + e -f.sort() -a = random.sample(xrange(100), 10) -b = random.sample(xrange(1000), 10) -c = random.sample(xrange(10000), 10) -d = random.sample(xrange(100000), 10) -e = random.sample(xrange(1000000), 10) -g = a + b + c + d + e - -testcases = [] -for item in f: - a = f[random.randrange(0, len(f))] - b = g[random.randrange(0, len(g))] - gc = gcd(a, b) - testcases.append([a, b, gc]) - -sortedcases = sorted(testcases, key=lambda case: case[0]) - -fil = open('/home/madhu/Desktop/gcdtest.dat', 'w') -for case in sortedcases: - fil.write('%d, %d, %d\n' % (case[0], case[1], case[2])) - -fil.close() diff --git a/lecture_notes/test_driven_development/lab_workbook.rst b/lecture_notes/test_driven_development/lab_workbook.rst deleted file mode 100644 index fa5afa4..0000000 --- a/lecture_notes/test_driven_development/lab_workbook.rst +++ /dev/null @@ -1,146 +0,0 @@ -====================================== -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 diff --git a/lecture_notes/test_driven_development/math_utils/gcd.py b/lecture_notes/test_driven_development/math_utils/gcd.py deleted file mode 100644 index 7204ac0..0000000 --- a/lecture_notes/test_driven_development/math_utils/gcd.py +++ /dev/null @@ -1,22 +0,0 @@ -def gcd(a, b): - """Returns the Greatest Common Divisor of the two integers - passed as arguments. - - Args: - a: an integer - b: another integer - - Returns: Greatest Common Divisor of a and b - - >>> gcd(48, 64) - 16 - >>> gcd(44, 19) - 1 - """ - if b == 0: - return b - return gcd(b, a%b) - -if __name__ == "__main__": - import doctest - doctest.testmod() diff --git a/lecture_notes/test_driven_development/math_utils/gcd_testcases.dat b/lecture_notes/test_driven_development/math_utils/gcd_testcases.dat deleted file mode 100644 index 3829b12..0000000 --- a/lecture_notes/test_driven_development/math_utils/gcd_testcases.dat +++ /dev/null @@ -1,50 +0,0 @@ -6, 22, 2 -6, 48744, 6 -14, 143295, 1 -22, 751, 1 -35, 79, 1 -35, 96, 1 -52, 12, 4 -73, 79, 1 -73, 184790, 1 -86, 11, 1 -93, 8, 1 -93, 798, 3 -113, 42785, 1 -209, 2135, 1 -395, 8989, 1 -587, 331, 1 -643, 751, 1 -721, 242525, 1 -733, 5622, 1 -854, 42785, 1 -1695, 57, 3 -1695, 798, 3 -3429, 177203, 1 -4603, 12, 1 -4603, 48744, 1 -6139, 57, 1 -6139, 204, 1 -6660, 96, 12 -6660, 410400, 180 -6703, 410400, 1 -8964, 22, 2 -9673, 751, 1 -9673, 7909, 1 -9673, 3335, 1 -16028, 891, 1 -44231, 378, 1 -49020, 751, 1 -57908, 184790, 2 -65482, 548045, 1 -79715, 8, 1 -79715, 891, 1 -79715, 66371, 1 -321807, 891, 3 -366607, 97, 1 -402212, 5595, 1 -448426, 66371, 1 -575271, 4617, 9 -575271, 402152, 1 -680256, 48744, 72 -779565, 184790, 5 diff --git a/lecture_notes/test_driven_development/math_utils/test_gcd.py b/lecture_notes/test_driven_development/math_utils/test_gcd.py deleted file mode 100644 index c81c72b..0000000 --- a/lecture_notes/test_driven_development/math_utils/test_gcd.py +++ /dev/null @@ -1,29 +0,0 @@ -import gcd -import unittest - -class TestGcdFunction(unittest.TestCase): - - def setUp(self): - self.test_file = open('gcd_testcases.dat') - self.test_cases = [] - for line in self.test_file: - values = line.split(', ') - a = int(values[0]) - b = int(values[1]) - g = int(values[2]) - - self.test_cases.append([a, b, g]) - - def test_gcd(self): - for case in self.test_cases: - a = case[0] - b = case[1] - g = case[2] - self.assertEqual(gcd.gcd(a, b), g) - - def tearDown(self): - self.test_file.close() - del self.test_cases - -if __name__ == '__main__': - unittest.main() diff --git a/lecture_notes/test_driven_development/tdd.rst b/lecture_notes/test_driven_development/tdd.rst deleted file mode 100644 index 6347c47..0000000 --- a/lecture_notes/test_driven_development/tdd.rst +++ /dev/null @@ -1,656 +0,0 @@ -======================= -Test Driven Development -======================= - -What is TDD? -============ - -Objectives ----------- -At the end of this section, you will be able to: - -1. Write your code using the TDD paradigm. -#. Use doctests to test your Python code. -#. Use unittests to test your Python code. -#. Use the nose module to test your code. - - -Test Driven Development (TDD), as the name suggests, is a style or method -of software development based on the idea of writing code, after writing -tests for them. As the tests are written for code, that doesn't even exist -yet, it is bound to fail. Actual code is later written, to pass the test -and later on refactored. - -The basic steps of TDD are roughly as follows - - -1. Decide upon the feature to implement and the methodology of testing it. -#. Write the tests for the feature decided upon. -#. Just write enough code, so that the test can be run, but it fails. -#. Improve the code, to just pass the test and at the same time passing all - previous tests. -#. Run the tests to see, that all of them run successfully. -#. Refactor the code we've just written -- optimize the algorithm, remove - duplication, add documentation, etc. -#. Run the tests again, to see that all the tests still pass. -#. Go back to 1. - -First "Test" -============ - -Now, that we have an overview of TDD, let's get down to writing our first -test. Let us consider a very simple program which returns the Greatest -Common Divisor (GCD) of two numbers. - -Before being able to write our test, we need to have a clear idea of the -code units our program will contain, so that we can test each of them. -Let's first define the code units that our GCD program is going to have. - -Our program is going to contain only one function called ``gcd``, which -will take in two arguments, and return a single value, which is the GCD of -the two arguments. So if we want to find out GCD of 44, 23, we call the -function, with the arguments 44 and 23. - -:: - - c = gcd(44, 23) - -c will contain the GCD of the two numbers. - -We have defined the code units in our program. So, we can go ahead and -write tests for it. - -When adopting the TDD methodology, it is important to have a clear set of -results defined for our code units i.e., for every given set of inputs as -that we wish use as a test case, we must have, before hand, the exact -outputs that are expected for those input test cases. We must not be -running around looking for outputs for our test cases after we have the -code ready, or even while we are writing the tests. - -Let one of our test cases be 48 and 64 as ``a`` and ``b``, respectively. -For this test case we know that the expected output, the GCD, is 16. Let -our second test case be 44 and 19 as ``a`` and ``b``, respectively. We know -that their GCD is 1, and that is the expected output. - -But before writing one, we should What does a test look like? What does it -do? Let's answer this question first. - -Tests are just a series of assertions which are either True or False -depending on the expected behaviour of the code and the actual behaviour. -Tests for out GCD function could be written as follows. - -:: - - tc1 = gcd(48, 64) - if tc1 != 16: - print "Failed for a=48, b=64. Expected 16. Obtained %d instead." % tc1 - exit(1) - - tc2 = gcd(44, 19) - if tc2 != 1: - print "Failed for a=44, b=19. Expected 1. Obtained %d instead." % tc2 - exit(1) - - print "All tests passed!" - -The next step is to run the tests we have written, but if we run the tests -now, the tests wouldn't even run. Why? We don't even have the function -``gcd`` defined, for it to be called by the tests. The test code doesn't -even run! What is the way out? We shall first write a very minimal -definition (or a stub) of the function ``gcd``, so that it can be called by -the tests. - -A minimal definition for the ``gcd`` function would look like this - -:: - - def gcd(a, b): - pass - -As you can see, the stub for ``gcd`` function does nothing, other than -define the required function which takes the two arguments a and b. No -action is done upon the arguments, passed to the function, yet. The -function definition is empty and just contains the ``pass`` statement. - -Let us put all these in a file and call this file ``gcd.py`` - -:: - - def gcd(a, b): - pass - - if __name__ == '__main__': - tc1 = gcd(48, 64) - if tc1 != 16: - print "Failed for a=48 and b=64. Expected 16. Obtained %d instead." % tc1 - exit(1) - - tc2 = gcd(44, 19) - if tc2 != 1: - print "Failed for a=44 and b=19. Expected 1. Obtained %d instead." % tc2 - exit(1) - - print "All tests passed!" - -Recall that, the condition ``__name__ == '__main__'`` becomes true, only -when the python script is ran directly as a stand-alone script. So any code -within this ``if`` block is executed only when the script is run as a -stand-alone script and doesn't run when it is used as a module and -imported. - -Let us run our code as a stand-alone script. - -:: - - $ python gcd.py - Traceback (most recent call last): - File "gcd.py", line 7, in <module> print "Failed for a=48 and b=64. Expected 16. Obtained %d instead." % tc1 - TypeError: %d format: a number is required, not NoneType - -We now have our tests, the code unit stub, and a failing test. The next -step is to write code, so that the test just passes. - -Let's us the algorithm given by a greek mathematician, 2300 years ago, the -Euclidean algorithm, to compute the GCD of two numbers. - -**Note**: If you are unaware of Euclidean algorithm to compute the gcd of -two numbers please refer to it on wikipedia. It has a very detailed -explanation of the algorithm and its proof of validity among other things. - -The ``gcd`` stub function can be modified to the following function, - -:: - - def gcd(a, b): - if a == 0: - return b - while b != 0: - if a > b: - a = a - b - else: - b = b - a - return a - -Now let us run our script which already has the tests written in it and see -what happens - -:: - - $ python gcd.py - All tests passed! - -Success! We managed to write code, that passes all the tests. The code we -wrote was simplistic, actually. If you take a closer look at the code you -will soon realize that the chain of subtraction operations can be replaced -by a modulo operation i.e. taking remainders of the division between the -two numbers since they are equivalent operations. The modulo operation is -far better, since it combines a lot of subtractions into one operation and -the reduction is much faster. Take a few examples, and convince yourself -that using the modulo operation is indeed faster than the subtraction -method. - -:: - - def gcd1(a, b): - while b != 0 and a != 0: - if a > b: - a = a%b - else: - b = b%a - if a == 0: - return b - else: - return a - -But we know that the modulo of ``a%b`` is always less than b. So, if the -condition ``a>b`` is True in this iteration, it will definitely be False in -the next iteration. Also note that , and in the case when ``a < b``, -``a%b`` is equal to ``a``. So, we could, essentially, get rid of the -``if-else`` block and combine a swapping operation along with the modulo -operation. - -:: - - def gcd(a, b): - while b != 0: - a, b = b, a % b - return a - -Let's run our tests again, and check that all the tests are passed, again. - -We could make one final -improvement to our ``gcd`` function, which is strictly not necessary in -terms of efficiency, but is definitely more readable and easy to -understand. We could make our function a recursive function, as shown below - -:: - - def gcd(a, b): - if b == 0: - return a - return gcd(b, a%b) - -Much shorter and sweeter! We again run all the tests, and check that they -are passed. It indeed does pass all the tests! - -But there is still one small problem. There is no way, for the users of -this function, to determine how to use it, how many arguments it takes and -what it returns, among other things. This is a handicap for the people -reading your code, as well. This is well written function, with no -documentation whatsoever. - -Let's add a docstring as shown, - -:: - - def gcd(a, b): - """Returns the Greatest Common Divisor of the two integers - passed as arguments. - - Args: - a: an integer - b: another integer - - Returns: Greatest Common Divisor of a and b - """ - if b == 0: - return a - return gcd(b, a%b) - -Now we have refactored our code enough to make it well written piece -of code. We have now successfully completed writing our first test, writing -the relevant code, ensuring that the tests are passed and refactoring the -code, until we are happy with the way it works and looks. - -Let's now build on what we have learnt so far and learn more about writing -tests and improve our methodology of writing tests and simplify the process -of writing them. - -Persistent Test Cases ---------------------- - -As already stated, tests should be predetermined and you should have your -test cases and their outputs ready, even before you write the first line of -code. This test data is repeatedly used in the process of writing code. So, -it makes sense to have the test data to be stored in some persistent format -like a database, a text file, a file of specific format like XML, etc. - -Let us modify the test code for the GCD function and save our test data in -a text file. Let us decide upon the following format for the test data. - - 1. The file has multiple lines of test data. - 2. Each line in this file corresponds to a single test case. - 3. Each line consists of three comma separated values -- - - i. First two coloumns are the integers for which the GCD has to - be computed - ii. Third coloumn is the expected GCD to the preceding two - numbers. - -Now, how do we modify our tests to use this test data? We read the file -line by line and parse it to obtain the required numbers whose GCD we wish -to calculate, and the expected output. We can now call the ``gcd`` function -with the correct arguments and verify the output with the expected output. - -Let us call our data file ``gcd_testcases.dat`` - -:: - - if __name__ == '__main__': - for line in open('gcd_testcases.dat'): - values = line.split(', ') - a = int(values[0]) - b = int(values[1]) - g = int(values[2]) - - tc = gcd(a, b) - if tc != g: - print "Failed for a=%d and b=%d. Expected %d. Obtained %d instead." % (a, b, g, tc) - exit(1) - - print "All tests passed!" - -When we execute the gcd.py script again we will notice that the code passes -all the tests. - -Now, we have learnt how to write simple test cases and develop code based -on these test cases. We shall now move on to learn to use some testing -frameworks available for Python, which make some of the task easier. - -Python Testing Frameworks -========================= - -Python provides two ways to test the code we have written. One of them is -the unittest framework and the the other is the doctest module. To start -with, let us discuss the doctest module. - -doctest -~~~~~~~ - -As we have already discussed, a well written piece of code must always be -accompanied by documentation. We document functions or modules using -docstrings. In addition to writing a generic description of the function or -the module, we can also add examples of using these functions in the -interactive interpreter to the docstrings. - -Using the ``doctest`` module, we can pick up all such interactive session -examples, execute them, and determine if the documented piece of code runs, -as it was documented. - -The example below shows how to write ``doctests`` for our ``gcd`` function. - -:: - - def gcd(a, b): - """Returns the Greatest Common Divisor of the two integers - passed as arguments. - - Args: - a: an integer - b: another integer - - Returns: Greatest Common Divisor of a and b - - >>> gcd(48, 64) - 16 - >>> gcd(44, 19) - 1 - """ - if b == 0: - return a - return gcd(b, a%b) - -That is all there is, to writing a ``doctest`` in Python. Now how do we use -the ``doctest`` module to execute these tests. That too, is fairly straight -forward. All we need to do is tell the doctest module to execute, using the -``doctest.testmod`` function. - -Let us place this piece of code at the same place where we placed our tests -earlier. So putting all these together we have our ``gcd.py`` module which -looks as follows - -:: - - def gcd(a, b): - """Returns the Greatest Common Divisor of the two integers - passed as arguments. - - Args: - a: an integer - b: another integer - - Returns: Greatest Common Divisor of a and b - - >>> gcd(48, 64) - 16 - >>> gcd(44, 19) - 1 - """ - if b == 0: - return a - return gcd(b, a%b) - - if __name__ == "__main__": - import doctest - doctest.testmod() - -The ``testmod`` function automatically picks up all the docstrings that -have sample sessions from the interactive interpreter, executes them and -compares the output with the results as specified in the sample sessions. -If all the outputs match the expected outputs that have been documented, it -doesn't say anything. It complains only if the results don't match as -documented, expected results. - -When we execute this script as a stand-alone script we will get back the -prompt with no messages which means all the tests passed - -:: - - $ python gcd.py - $ - -If we further want to get a more detailed report of the tests that were -executed we can run python with -v as the command line option to the script - -:: - - $ python gcd.py -v - Trying: - gcd(48, 64) - Expecting: - 16 - ok - Trying: - gcd(44, 19) - Expecting: - 1 - ok - 1 items had no tests: - __main__ - 1 items passed all tests: - 2 tests in __main__.gcd - 2 tests in 2 items. - 2 passed and 0 failed. - Test passed. - - -**Note:** We can have the sample sessions as test cases as long as the -outputs of the test cases do not contain any blank lines. In such cases we -may have to use the exact string ``<BLANKLINE>`` - -For the sake of illustrating a failing test case, let us assume that we -made a small mistake in our code. Instead of returning ``a`` when b = 0, we -typed it as ``return b`` when b = 0. Now, all the GCDs returned will have -the value 0. The code looks as follows - -:: - - def gcd(a, b): - """Returns the Greatest Common Divisor of the two integers - passed as arguments. - - Args: - a: an integer - b: another integer - - Returns: Greatest Common Divisor of a and b - - >>> gcd(48, 64) - 16 - >>> gcd(44, 19) - 1 - """ - if b == 0: - return b - return gcd(b, a%b) - -Executing this code snippet without -v option to the script - -:: - - $ python gcd.py - ********************************************************************** - File "gcd.py", line 11, in __main__.gcd - Failed example: - gcd(48, 64) - Expected: - 16 - Got: - 0 - ********************************************************************** - File "gcd.py", line 13, in __main__.gcd - Failed example: - gcd(44, 19) - Expected: - 1 - Got: - 0 - ********************************************************************** - 1 items had failures: - 2 of 2 in __main__.gcd - ***Test Failed*** 2 failures. - -The output clearly complains that there were exactly two test cases -that failed. If we want a more verbose report we can pass -v option to -the script. - -This is all there is, to using the ``doctest`` module in Python. -``doctest`` is extremely useful when we want to test each Python function -or module individually. - -For more information about the doctest module refer to the Python library -reference on doctest[0]. - -unittest framework -~~~~~~~~~~~~~~~~~~ - -We needn't go too far ahead, to start complaining that ``doctest`` is not -sufficient to write complicated tests especially when we want to automate -our tests, write tests that need to test for more convoluted code pieces. -For such scenarios, Python provides a ``unittest`` framework. - -The ``unittest`` framework provides methods to efficiently automate tests, -easily initialize code and data for executing the specific tests, cleanly -shut them down once the tests are executed and easy ways of aggregating -tests into collections and better way of reporting the tests. - -Let us continue testing our gcd function in the Python module named -``gcd.py``. The ``unittest`` framework expects us to subclass the -``TestCase`` class in ``unittest`` module and place all our test code as -methods of this class. The name of the test methods are expected to be -started with ``test_``, so that the test runner knows which methods are to -be executed as tests. We shall use the test cases supplied by -``gcd_testcases.dat``. - -Lastly, to illustrate the way to test Python code as a module, let's create -a new file called ``test_gcd.py`` following the same convention used to -name the test methods, and place our test code in it. - -:: - - import gcd - import unittest - - class TestGcdFunction(unittest.TestCase): - - def setUp(self): - self.test_file = open('gcd_testcases.dat') - self.test_cases = [] - for line in self.test_file: - values = line.split(', ') - a = int(values[0]) - b = int(values[1]) - g = int(values[2]) - - self.test_cases.append([a, b, g]) - - def test_gcd(self): - for case in self.test_cases: - a = case[0] - b = case[1] - g = case[2] - self.assertEqual(gcd.gcd(a, b), g) - - def tearDown(self): - self.test_file.close() - del self.test_cases - - if __name__ == '__main__': - unittest.main() - -Please note that although we highly recommend writing docstrings for all -the classes, functions and modules, we have not done so, in this example to -keep it compact. Adding suitable docstrings wherever required is left to -you, as an exercise. - -It would be a waste, to read the test data file, each time we run a test -method. So, in the setUp method, we read all the test data and store it -into a list called test_cases, which is 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 stated earlier, the ``test_gcd`` method. Note that, we import -the ``gcd`` Python module we have written at the top of this test file and -from this test method we call the ``gcd`` function within the ``gcd`` -module to be tested with the each set of ``a`` and ``b`` values -``test_cases``. Once we execute the ``test_gcd`` function, we obtain the -result and compare it with the expected result as stored in the -corresponding ``test_cases`` attribute using the ``assertEqual`` method -provided by ``TestCase`` class in the ``unittest`` framework. There are -several other assertion methods supplied by the unittest framework. For a -more detailed information about this, refer to the unittest library -reference at [1]. This brings us to the end of our discussion of the -``unittest`` framework. - -nose -==== - -We have seen the ``unittest`` frame work and Python ``doctest`` module. . -One problem however remains. It is not easy to organize, choose and run -tests, when our code is scattered across multiple files. In the real world -scenario, this is often the case. - -In such a such a scenario, a tool which can aggregate these tests -automatically, and run them. The ``nose`` module, does precisely this, for -us. It can aggregate ``unittests`` and ``doctests`` into a collection and -run them. It also helps output the test-results and aggregate them in -various formats. - -Although nose is not part of the standard Python distribution itself, it -can be very easily installed by using easy_install command as follows - -:: - - $ easy_install nose - -Or download the nose package from [2], extracting the archive and running -the command from the extracted directory - -:: - - $ python setup.py install - -Now we have nose up and running, but how do we use it? We shall use the -``nosetests`` command provided by the ``nose`` module, in the top-level -directory of our code. - -:: - - $ nosetests - -That's all! ``nose`` will now automatically pick all the tests in all the -directories and subdirectories in our code base and execute them. - -However if we want to execute specific tests we can pass the test file -names or the directories as arguments to nosetests command. For a detailed -explanation about this, refer to [3] - -Conclusion -========== - -Now we have all the trappings we want to write state-of-the art tests. To -emphasize the same point again, any code which was written before writing -the test and the testcases in hand is flawed by design. So it is -recommended to follow the three step approach while writing code for any -project as below: - - 1. Write failing tests with testcases in hand. - 2. Write the code to pass the tests. - 3. Refactor the code for better performance. - -This approach is very famously known to the software development world as -"Red-Green-Refactor" approach[4]. - -[0] - http://docs.python.org/library/doctest.html -[1] - http://docs.python.org/library/unittest.html -[2] - http://pypi.python.org/pypi/nose/ -[3] - http://somethingaboutorange.com/mrl/projects/nose/0.11.2/usage.html -[4] - http://en.wikipedia.org/wiki/Test-driven_development - -.. - Local Variables: - mode: rst - indent-tabs-mode: nil - sentence-end-double-space: nil - fill-column: 75 - End: |