"metadata": {},
"source": [
"That was a helpful error message. \n",
- "This tells you that the error occurred in line 2 (notice the error) and then below you are given a message about the nature of your error."
+ "This tells you that the error occurred in line 2 (notice the arrow) and then below you are given a message about the nature of your error."
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "metadata": {},
+ "source": [
+ "A more complicated example:"
]
},
{
"collapsed": false,
"input": [
"def add_nums(a, b):\n",
+ " '''adds input numbers a and b'''\n",
" return a+b\n",
+ "\n",
"def multiply(a, b, d):\n",
+ " '''multiplies the sum of a and b by d'''\n",
" return add_nums(a, b)*d\n",
+ "\n",
"def divide_num(a, b, d, g):\n",
+ " '''divides the product of the sum of a and b and d by g'''\n",
" return multiply(a, b, d)/g\n",
"\n",
"def arithmatic(num1, num2, num3, num4):\n",
+ " '''returns ((num1 + num2)*num3)/num4'''\n",
" total = divide_num(num1, num2, num3, num4)\n",
" return total\n",
"\n",
"prompt_number": 10
},
{
- "cell_type": "raw",
+ "cell_type": "markdown",
"metadata": {},
"source": [
"Starting at the bottom:\n",
- "Based on the error message, it looks like we tried to add a string and an integer in add num.\n",
- "Moving our way up, add_num was called by multiply\n",
- "multiply was called by divide_num\n",
- "and divide_num was called by arithmetic\n",
- "We passed arithmetic ('a', 2, 'b', 'd')\n",
- "arithmetic passed divide_num('a', 2, 'b', 'd')\n",
- "divide_num passed multiply('a', 2, 'd')\n",
- "multiply passed add_num('a', 2) and there we see the problem - you can't add 'a' and 2"
+ "\n",
+ "- Based on the error message, it looks like we tried to add a string and an integer in add num.\n",
+ "- Moving our way up, **add_num** was called by multiply\n",
+ "- **multiply** was called by **divide_num**\n",
+ "- **divide_num** was called by **arithmetic**\n",
+ "- We passed **arithmetic** ('a', 2, 'b', 'd')\n",
+ "- **arithmetic** passed **divide_num** ('a', 2, 'b', 'd')\n",
+ "- **divide_num** passed **multiply**('a', 2, 'd')\n",
+ "- **multiply** passed **add_num(**'a', 2) and there we see the problem - you can't add 'a' and 2"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
- "def add_num(a, b):\n",
+ "def add_nums(a, b):\n",
+ " '''adds input numbers a and b'''\n",
" return a+b\n",
+ "\n",
"def multiply(a, b, d):\n",
+ " '''multiplies the sum of a and b by d'''\n",
" return add_nums(a, b)*d\n",
+ "\n",
"def divide_num(a, b, d, g):\n",
+ " '''divides the product of the sum of a and b and d by g'''\n",
" return multiply(a, b, d)/g\n",
"\n",
"def arithmatic(num1, num2, num3, num4):\n",
+ " '''returns ((num1 + num2)*num3)/num4'''\n",
" for num in [num1, num2, num3, num4]:\n",
" assert (type(num) is float) or (type(num) is int), str(num)+\" is not a float or integer type\"\n",
" total = divide_num(num1, num2, num3, num4)\n",
"outputs": [],
"prompt_number": 17
},
+ {
+ "cell_type": "raw",
+ "metadata": {},
+ "source": [
+ "Now when we try to enter strings as input we get an error message"
+ ]
+ },
{
"cell_type": "code",
"collapsed": false,
"metadata": {},
"source": [
"#Exercise\n",
- "1. Find the error in traceback_example.py from the traceback generated from typing (python traceback_example.py)"
+ "1. Find the error in traceback_example.py from the traceback generated from typing (python traceback_example.py 3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "#Unit Testing"
+ "--------------------\n",
+ "Solution:"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
- "Tracebacks are great for errors which python recongnizes as an error. You may however write valid code which does not do what you want it to do. For example in the code add_num, we intended this to add numbers, but it will happily add strings without producing an exception and traceback"
+ "python traceback_example.py 3\n",
+ "Traceback (most recent call last):\n",
+ " File \"traceback_example.py\", line 29, in <module>\n",
+ " diff_area_circle_area_square(r)\n",
+ " File \"traceback_example.py\", line 23, in diff_area_circle_area_square\n",
+ " area_sq = area_of_a_square(radius)\n",
+ " File \"traceback_example.py\", line 14, in area_of_a_square\n",
+ " return (2.0*radius) **2\n",
+ "NameError: global name 'radius' is not defined\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The error is in line 14 in the **area_of_a_square** function (this is called by line 23 of **diff_area_circle_area_square** which in turn is called by line 29 of *traceback_example.py*). The error message tells us that radius is not defined. Looking closely at **area_of_a_square** you can see that we should have used half_side rather than radius in the return statement"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#Unit Testing"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Tracebacks are great for errors which python recongnizes as an error. You may however write valid code which does not do what you want it to do (a computer will always do what you tell it to do, it just might not be what you think you're telling it). For example in the code **add_num**, we intended this to add numbers, but it will happily add strings without producing an exception and traceback"
]
},
{
"prompt_number": 20
},
{
- "cell_type": "raw",
+ "cell_type": "markdown",
"metadata": {},
"source": [
- "unit testing is writing at least one test for each function you write. This enables you to test that it is working as you expected it to, to think of cases you may not have considered, and to immediately know if a change you made in one location broke something somewhere else."
+ "*unit testing* is writing at least one test for each function you write. This enables you to:\n",
+ "\n",
+ "- test that it is working as you expected it to\n",
+ "- think of cases you may not have considered\n",
+ "- immediately know if a change you made in one location broke something somewhere else."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
+ "#write test function\n",
"def test_read_file():\n",
+ " '''\n",
+ " This is a test function to determine if\n",
+ " the read_file function correctly reads \n",
+ " the file test.txt\n",
+ " '''\n",
" all_lines = read_file('test.txt')\n",
" if all_lines[0] != '2, 3, 4, 5':\n",
" print 'first line is not \"2, 3, 4, 5\"'\n",
" print 'TEST FAILED ON LINE 1'\n",
" return \n",
+ " \n",
" if all_lines[1] != '1, 2, 6, 1, 2':\n",
" print 'second line is not \"1, 2, 6, 1, 2\"'\n",
" print 'TEST FAILED ON LINE 2'\n",
" return\n",
+ " \n",
" if all_lines[2] != '20, 9':\n",
" print 'third line is not \"20, 9\"'\n",
" print 'TEST FAILED ON LINE 3'\n",
"metadata": {},
"outputs": []
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Be smart about testing**: you probably don't need all 3 tests. If the function works for one line, it will work for all 3. Test a variety of cases which produce different behavior"
+ ]
+ },
{
"cell_type": "code",
"collapsed": false,
"collapsed": false,
"input": [
"def test_create_list():\n",
+ " '''\n",
+ " This function tests the function create_list, \n",
+ " to see if giving a list as a single string, \n",
+ " correctly parses and converts it to a list of floats\n",
+ " '''\n",
" ilist = create_list('2, 3, 5, 6, 0')\n",
" if ilist != [2, 3, 5, 6, 0]:\n",
" print 'TEST OF CREATE_LIST FAILED'\n",
]
},
{
- "cell_type": "raw",
+ "cell_type": "markdown",
"metadata": {},
"source": [
- "As a class we will come up with 3 tests - all positive numbers, what if there is a negative number, what if the total is zero, is zero in the list ok?\n"
+ "As a class we will come up with 3 tests."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "-------------\n",
+ "Possible Solution:\n",
+ "\n",
+ "- all positive numbers\n",
+ "- what if there is a negative number\n",
+ "- what if the total is zero, is zero in the list ok?"
]
},
{
"outputs": []
},
{
- "cell_type": "raw",
+ "cell_type": "markdown",
"metadata": {},
"source": [
- "Thinking up tests allows you to think about possible scenarios you wouldn't have normally considered. Test driven development asks you to think about your test cases before you write your code so that you can consider some of the possibilities. For instance - do you want to have sum_list return a negative number or always instist on a positive number"
+ "Thinking up tests allows you to think about possible scenarios you wouldn't have normally considered. *Test driven* development asks you to think about your test cases before you write your code so that you can consider some of the possibilities. For instance - do you want to have sum_list return a negative number or always insist on a positive number"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
- "What if you can't figure out what is wrong with your code from the traceback and you can't unit test because you can't compile your code or your test is failing and you can't figure out why? You can use the python debugger (pdb). There are a lot of things you can do with this, by far the function I use the most is pdb.set_trace(). This will halt execution at the line in the code where you place it and allow you to work interactively."
+ "What if you can't figure out what is wrong with your code from the traceback and you can't unit test because you can't compile your code or your test is not returning the result you expected and you can't figure out why? You can use the python debugger (pdb). There are a lot of things you can do with this, by far the function I use the most is pdb.set_trace(). This will halt execution at the line in the code where you place it and allow you to work interactively."
]
},
{
"metadata": {},
"source": [
"When pdb stops execution type:\n",
- ">>>print total\n",
- ">>>print len(ilist)\n",
+ ">>> print total\n",
+ ">>> print len(ilist)\n",
">>> print total/len(ilist)\n",
- ">>>print type(total)\n",
- ">>>print type(ilist)\n",
+ ">>> print type(total)\n",
+ ">>> print type(ilist)\n",
"\n",
"Ah ha, the problem is that they are both integers"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#Exercise:\n",
+ "Run the program pdb_example.py by typing *python pdb_example.py* followed by 4 positive numbers (without commas) into your command line.\n",
+ "\n",
+ "$python pdb_example.py 3 4 5 6\n",
+ "\n",
+ "Look at the code. Did it do what you expected? If not, use pdb.set_trace() to determine why not.\n",
+ "If it worked the way you expected, the try writting some assertion statements to catch possible problem input cases."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "---------------------------\n",
+ "Solution: This is an example of modifying a mutable object. You can ignore the call to **function1** the assignment to x, y, and z are overwritten by the call to **function2**. **function2** has both x and z pointing to the same object. Therefore, when you modify z, you also modify x"
+ ]
+ },
{
"cell_type": "code",
"collapsed": false,