Python/C4/Testing-and-debugging/English
Visual Cue | Narration |
---|---|
Show Slide 1
Containing title, name of the production team along with the logo of MHRD |
Hello Friends and Welcome to the tutorial on 'Testing and Debugging'. |
Show Slide 2
Learning objective |
At the end of the tutorial, you will be able to,
|
Show Slide 3
Pre-requisite slide |
Before beginning this tutorial,we would suggest you to complete the tutorial on "Getting started with functions" and "Advanced Features of Functions". |
Now, what is software testing? Software testing is an activity aimed at evaluating a program and determining that it meets its required results. | |
Show Slide 4
gcd function def gcd(a, b): if b == 0: return a Definition list ends without a blank line; unexpected unindent. return gcd(b, a%b) Pause for some time and then continue |
Lets first write a simple function to calculate gcd of two numbers. Open an editor and type the code shown on the slide and save it as gcd.py |
Save the file as gcd.py | Save the file as gcd.py in /home/fossee/ path |
Now we need to evaluate this function. That is, we have to check whether this function successfully gives us the gcd of two whole numbers. Thus we need a set of inputs and the exact outputs that are expected for those input test cases.
Let our test case be 48 and 64 as a and b respectively. For this test case we know that the GCD is 16. So that is the expected output. | |
Show Slide 5
Test for gcd.py def gcd(a, b): if b == 0: return a Definition list ends without a blank line; unexpected unindent. return gcd(b, a%b) if __name__ == '__main__': result = gcd(48, 64) if result != 16: Unexpected indentation. print "Test failed" Block quote ends without a blank line; unexpected unindent. print "Test Passed" Pause for some time and then continue |
Let us include code for testing in our file gcd.py and add the remaining lines of code to the file. |
Open a terminal
python /home/fossee/gcd.py |
Let us now run the script and test our code We run the code by providing the entire path where the file is located. |
We get the output as 'test passed' which means our code is correct. Note that we have introduced a new semantic which uses two new magic names in Python __name__ and __main__. This is a very common idiom used in Python. Every Python code in a file can be run in two ways: Either as an independent stand-alone script or as a Python module which can be imported by other Python scripts or modules. | |
Show Slide 6
Idiom |
When the idiom
if __name__ == '__main__': is used, the code within this if block is executed first when we run the Python file as a stand-alone script. In other words, when we run this python file as a stand-alone script, the control of the program first starts from the code that is within this if block after which the control is transferred to other parts of the program or to other modules from here. This comes as an extremely handy feature especially when we want to test our modules individually. But there can be a number of places where the gcd function might break. Would we have to write a separate test case for all of them. Pause the video here, try out the following exercise and resume the video. |
Show Slide 7
Assignment 1 |
Write code for gcd and write tests for it |
Well thats where automating tests come in. We can run many tests to check where our code can break. Lets see this with an example. First lets try and automate tests on the gcd function. For this we will write a file with test cases and call the function for all of them. | |
Show Slide 8
Structure of file 12 | 28 | 4 | 18 | 36 | 18 | 4678 | 39763 | 2339 |
|
The file structure is shown in form a table here.
The structure of the file will be the two parameters and the output result separated by space |
Open the file testcases.txt and show | We have separated the elements by a space. |
Show Slide 9
Code piece if __name__ == '__main__': for line in open('testcases.txt'): numbers = line.split() x = int(numbers[0]) y = int(numbers[1]) result = int(numbers[2]) if gcd(x, y) != result: print "Failed gcd test for", x, y else: print "Test passed", result |
We add this code piece to automate the test. |
Let us now test this code. Open the file gcd.py which we had created before and add this piece of code accordingly. | Open the file gcd.py and add the above piece of code appropriately |
Switch to terminal
python /home/fossee/gcd.py |
Now, we run it as, |
We see that our code has passed the test.
Pause the video here, try out the following exercise and resume the video. | |
Show Slide 10
Assignment 2 |
For the same inputs as gcd write automated tests for LCM. |
Show Slide 11
Solution 2 def gcd(a, b): if a % b == 0: return b Definition list ends without a blank line; unexpected unindent. return gcd(b, a%b) def lcm(a, b): return (a * b) / gcd(a, b) if __name__ == '__main__': for line in open('lcmtestcases.txt'): numbers = line.split() x = int(numbers[0]) y = int(numbers[1]) result = int(numbers[2]) if lcm(x, y) != result: Unexpected indentation. print "Failed lcm test for", x, y Block quote ends without a blank line; unexpected unindent. else: print "Test passed", result Pause for some time and then continue |
We shall make use of the same automated test code which we had used for GCD with minor changes. Use the data from the file lcmtestcases.txt . The solution is on your screen. |
This is the complete solution for the problem You can test this code by running it on your terminal as we had done for gcd.py | |
Thus, for any program there can be innumerable test cases. Hence practically, it is not possible to test cases. However there are many ideas to reduce the set of test cases by testing those cases that are
Unexpected indentation. more likely to show errors. Moving from testing lets talk a bit about coding style now. Apart from from being able to perform the required task, a property of a good program is its readability. Code is read more often than it is written. This is because, that way, other people can learn from it and extend and improve it. There are certain pointers for readable code that I am going to discuss. First, Naming variables. | |
Show Slide 12
Meaning full names |
We choose a name so that it becomes easier to understand it's usage. Lets look at this with an example
amount = 12.68 denom = 0.05 nCoins = round(amount/denom) rAmount = nCoins * denom As we can see in the example it is very easy to make what the code is doing. One can almost read it as English sentences. Amount is 12.68 Denomination is .05 Number of coins is round of amount by denominations. Proper naming helps so much in understanding the code. |
Show Slide 13
Code style points |
Also one should keep in mind the following things while writing a code.
Pause the video here, try out the following exercise and resume the video. |
Show Slide 14
Assignment 3 |
Give meaningful names to the variables in following code
c=a/b |
Show Slide 15
Solution 3 |
The solution is on your screen.
As you saw, this will help enormously towards making our program more readable. |
Open the terminal and navigate to the current location you are working in
ipython while True print 'Hello world' |
Now let us move on to handling errors and exceptions. Lets try out the following piece of code |
1/0 | So what happens when we do this on the interpreter. The interpreter says that this is a syntax error. Syntax error are caused when we do not follow the rules of the programming language.
However lets try an expression like |
Open another terminal and type ipython
ipython a = raw_input("Enter a number:") Enter a non-numeric input num = int(a) |
Although this expression follows the programming language rules, however it is not possible to express the solution of this expression. Thus python throws an exception called ZeroDivisionError. Exception is special kind of failure reported by the programming language.
Lets see why and how we can use Exception in our programs. |
You will notice that when you run this program and give and non-numeric input it throws a 'ValueError' Exception. | |
Show Slide 16
Code snippet |
So now we can 'catch' this exception and write code to handle it. |
a = raw_input("Enter a number")
Enter a decimal number try: num = int(a) except: print "Wrong input ..." |
For this we have try and except clause in python. Lets change our previous code slightly. |
In this piece of code, python tries to run the code inside the try block but when if it fails it executes the code block in except. In previous example we encountered a problem with running our conversion to integer code. We found out what caused the error and then deviced a solution for it. This whole process is called debugging.
One can understand the debugging process using the figure. In debugging process, we form a hypothesis of what causes the error. Test if it is correct by changing the code. And refine the hypothesis on the basis of our result. | |
Open an editor and type the following code
def test(): total=1+1 print spam Save it as file mymodule.py |
Lets see another example of debugging. Create a file mymodule.py and add the following code |
import mymodule
mymodule.test() |
Lets now try and run this code on the ipython interpreter |
Show Slide 17
idb and total being accessed |
Interpreter gives us an error because spam is not defined. |
%debug | lets now do %debug on ipython interpreter. |
total | The prompt on the shell has changed to ipdb. This is debugger here you can access variables in that code block for example 'total'unlike the normal interpreter. Type, |
We get the correct output. To exit from the ipdb prompt, press q | |
Show Slide 18
Summary slide |
This brings us to the end of this tutorial. In this tutorial,we have learnt to,
|
Show Slide 19
Self assessment questions slide |
Here are some self assessment questions for you to solve
|
Show Slide 20
Solution of self assessment questions on slide |
And the answers,
2. We start the debugger on ipython by saying, %debug
|
Show Slide 21
Acknowledgment slide |
Hope you have enjoyed this tutorial and found it useful. Thank you! |