Initial lesson plan for testing and debugging covering: unit testing, tracebacks...
authorAzalee Bostroem <abostroem@gmail.com>
Tue, 20 Nov 2012 04:24:50 +0000 (23:24 -0500)
committerW. Trevor King <wking@tremily.us>
Fri, 1 Nov 2013 03:36:08 +0000 (20:36 -0700)
unit_testing/PresenterNotes.ipynb [new file with mode: 0644]
unit_testing/traceback_example.py [new file with mode: 0644]

diff --git a/unit_testing/PresenterNotes.ipynb b/unit_testing/PresenterNotes.ipynb
new file mode 100644 (file)
index 0000000..6be1c2d
--- /dev/null
@@ -0,0 +1,551 @@
+{
+ "metadata": {
+  "name": "PresenterNotes"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "#Trace Backs"
+     ]
+    },
+    {
+     "cell_type": "raw",
+     "metadata": {},
+     "source": [
+      "When we try to run code which contains an error (which makes it uninterpretable by your computer) python will give you a traceback. This is a trail of error messages. At the top is the original function which called to the function which created the error. There can be many nests within nests. Usually the most important part is the last line of the trace back"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "#simplest example\n",
+      "print float('a')"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "ename": "ValueError",
+       "evalue": "could not convert string to float: a",
+       "output_type": "pyerr",
+       "traceback": [
+        "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
+        "\u001b[0;32m<ipython-input-3-14014d37c62a>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;31m#simplest example\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mprint\u001b[0m \u001b[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'a'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+        "\u001b[0;31mValueError\u001b[0m: could not convert string to float: a"
+       ]
+      }
+     ],
+     "prompt_number": 3
+    },
+    {
+     "cell_type": "raw",
+     "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."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "def add_nums(a, b):\n",
+      "    return a+b\n",
+      "def multiply(a, b, d):\n",
+      "    return add_nums(a, b)*d\n",
+      "def divide_num(a, b, d, g):\n",
+      "    return multiply(a, b, d)/g\n",
+      "\n",
+      "def arithmatic(num1, num2, num3, num4):\n",
+      "    total = divide_num(num1, num2, num3, num4)\n",
+      "    return total\n",
+      "\n",
+      "print arithmatic('a', 2, 'b', 'd')"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "ename": "TypeError",
+       "evalue": "cannot concatenate 'str' and 'int' objects",
+       "output_type": "pyerr",
+       "traceback": [
+        "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
+        "\u001b[0;32m<ipython-input-10-d3a950f76003>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m     10\u001b[0m     \u001b[0;32mreturn\u001b[0m \u001b[0mtotal\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     11\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m \u001b[0;32mprint\u001b[0m \u001b[0marithmatic\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'a'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'b'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'd'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+        "\u001b[0;32m<ipython-input-10-d3a950f76003>\u001b[0m in \u001b[0;36marithmatic\u001b[0;34m(num1, num2, num3, num4)\u001b[0m\n\u001b[1;32m      7\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      8\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0marithmatic\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m     \u001b[0mtotal\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdivide_num\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     10\u001b[0m     \u001b[0;32mreturn\u001b[0m \u001b[0mtotal\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     11\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
+        "\u001b[0;32m<ipython-input-10-d3a950f76003>\u001b[0m in \u001b[0;36mdivide_num\u001b[0;34m(a, b, d, g)\u001b[0m\n\u001b[1;32m      4\u001b[0m     \u001b[0;32mreturn\u001b[0m \u001b[0madd_nums\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdivide_num\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0md\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0mmultiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0md\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      7\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      8\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0marithmatic\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+        "\u001b[0;32m<ipython-input-10-d3a950f76003>\u001b[0m in \u001b[0;36mmultiply\u001b[0;34m(a, b, d)\u001b[0m\n\u001b[1;32m      2\u001b[0m     \u001b[0;32mreturn\u001b[0m \u001b[0ma\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0mb\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mmultiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0md\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0madd_nums\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      5\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdivide_num\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0md\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      6\u001b[0m     \u001b[0;32mreturn\u001b[0m \u001b[0mmultiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0md\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+        "\u001b[0;32m<ipython-input-10-d3a950f76003>\u001b[0m in \u001b[0;36madd_nums\u001b[0;34m(a, b)\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0madd_nums\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0ma\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0mb\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mmultiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0md\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m     \u001b[0;32mreturn\u001b[0m \u001b[0madd_nums\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdivide_num\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0md\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+        "\u001b[0;31mTypeError\u001b[0m: cannot concatenate 'str' and 'int' objects"
+       ]
+      }
+     ],
+     "prompt_number": 10
+    },
+    {
+     "cell_type": "raw",
+     "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"
+     ]
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "##Assertion statements"
+     ]
+    },
+    {
+     "cell_type": "raw",
+     "metadata": {},
+     "source": [
+      "We can add a statement requiring all inputs to be floating point or integers. Such a statement is called an assert statement"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "def add_num(a, b):\n",
+      "    return a+b\n",
+      "def multiply(a, b, d):\n",
+      "    return add_nums(a, b)*d\n",
+      "def divide_num(a, b, d, g):\n",
+      "    return multiply(a, b, d)/g\n",
+      "\n",
+      "def arithmatic(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",
+      "    return total\n",
+      "\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 17
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "print arithmatic('a', 2, 'b', 'd')"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "ename": "AssertionError",
+       "evalue": "a is not a float or integer type",
+       "output_type": "pyerr",
+       "traceback": [
+        "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mAssertionError\u001b[0m                            Traceback (most recent call last)",
+        "\u001b[0;32m<ipython-input-18-7e7770598bdf>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mprint\u001b[0m \u001b[0marithmatic\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'a'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'b'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'd'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+        "\u001b[0;32m<ipython-input-17-6802eba7e608>\u001b[0m in \u001b[0;36marithmatic\u001b[0;34m(num1, num2, num3, num4)\u001b[0m\n\u001b[1;32m      8\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0marithmatic\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      9\u001b[0m     \u001b[0;32mfor\u001b[0m \u001b[0mnum\u001b[0m \u001b[0;32min\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mnum1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 10\u001b[0;31m         \u001b[0;32massert\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mtype\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0mfloat\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mtype\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;34m\" is not a float or integer type\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     11\u001b[0m     \u001b[0mtotal\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdivide_num\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     12\u001b[0m     \u001b[0;32mreturn\u001b[0m \u001b[0mtotal\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+        "\u001b[0;31mAssertionError\u001b[0m: a is not a float or integer type"
+       ]
+      }
+     ],
+     "prompt_number": 18
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "#Exercise\n",
+      "1. Find the error in traceback_example.py from the traceback generated from typing (python traceback_example.py)"
+     ]
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "#Unit Testing"
+     ]
+    },
+    {
+     "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"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "print add_num(1, 2)\n",
+      "print add_num('a', 'b')"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "3\n",
+        "ab\n"
+       ]
+      }
+     ],
+     "prompt_number": 20
+    },
+    {
+     "cell_type": "raw",
+     "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."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "#Create skeleton:\n",
+      "\n",
+      "#read in file\n",
+      "#create a list of floats for each line\n",
+      "#run scan function"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "#read in file\n",
+      "def read_file(filename):\n",
+      "    ofile = open(filename, 'r')\n",
+      "    all_lines = ofile.readlines()\n",
+      "    return all_lines"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "raw",
+     "metadata": {},
+     "source": [
+      "How can we testing this function?\n",
+      "Let's write a test which tests this function:"
+     ]
+    },
+    {
+     "cell_type": "raw",
+     "metadata": {},
+     "source": [
+      "#Create a test file: test.txt\n",
+      "2, 3, 4, 5\n",
+      "1, 3, 6, 1, 2\n",
+      "20, 9"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "def test_read_file():\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",
+      "    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",
+      "    if all_lines[2] != '20, 9':\n",
+      "        print 'third line is not \"20, 9\"'\n",
+      "        print 'TEST FAILED ON LINE 3'\n",
+      "        return"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 2
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "#Now let's test read_file:\n",
+      "test_read_file()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "#So our read function works, now we need to create a list of floats from each line"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "def create_list(iline):\n",
+      "    ilist = []\n",
+      "    for num in iline.split():\n",
+      "        ilist.append(float(num))\n",
+      "    return ilist"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "raw",
+     "metadata": {},
+     "source": [
+      "Now we have a new \"unit\" to test\n",
+      "How can we test this new function? \n",
+      "BRAINSTORM AS A CLASS"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "def test_create_list():\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",
+      "    return"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "#Let's test create_list\n",
+      "test_create_list()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "raw",
+     "metadata": {},
+     "source": [
+      "Finally, we are ready to write our sum function"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "def sum_list(ilist):\n",
+      "    total = 0\n",
+      "    for num in ilist:\n",
+      "        total = total + num\n",
+      "    return total"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "#Exercise:\n",
+      "1. What test(s) would you create to make sure that sum_list works correctly. You don't have to write the code, just come up with the test cases"
+     ]
+    },
+    {
+     "cell_type": "raw",
+     "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"
+     ]
+    },
+    {
+     "cell_type": "raw",
+     "metadata": {},
+     "source": [
+      "We will have these tests run everytime the code is run. This way, if any change we make changes the basic behavior of the function, we will know right away"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "#Add\n",
+      "test_read_file()\n",
+      "test_create_list()\n",
+      "test_sum_list()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "raw",
+     "metadata": {},
+     "source": [
+      "For instance, if we decide to change create_list to make a list of integers rather than floats, our test will fail and we will have to decide to either change the code or change the test:"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "def create_list(iline):\n",
+      "    ilist = []\n",
+      "    for num in iline.split():\n",
+      "        ilist.append(int(num))\n",
+      "    return ilist\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "raw",
+     "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"
+     ]
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "#PDB"
+     ]
+    },
+    {
+     "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."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "def calc_mean(ilist):\n",
+      "    total = 0\n",
+      "    for num in ilist:\n",
+      "        total = total + num\n",
+      "    return(total/len(ilist))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 35
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "ilist = [1, 2, 3, 4]\n",
+      "def test_calc_mean(ilist):\n",
+      "    assert calc_mean(ilist) == 2.5, 'mean of [1, 2, 3, 4] is not 2.5'\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 36
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "test_calc_mean(ilist)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "ename": "AssertionError",
+       "evalue": "mean of [1, 2, 3, 4] is not 2.5",
+       "output_type": "pyerr",
+       "traceback": [
+        "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mAssertionError\u001b[0m                            Traceback (most recent call last)",
+        "\u001b[0;32m<ipython-input-37-cbbaa632e3a9>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtest_calc_mean\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0milist\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+        "\u001b[0;32m<ipython-input-36-3da238d72bbb>\u001b[0m in \u001b[0;36mtest_calc_mean\u001b[0;34m(ilist)\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0milist\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mtest_calc_mean\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0milist\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m     \u001b[0;32massert\u001b[0m \u001b[0mcalc_mean\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0milist\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m2.5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'mean of [1, 2, 3, 4] is not 2.5'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+        "\u001b[0;31mAssertionError\u001b[0m: mean of [1, 2, 3, 4] is not 2.5"
+       ]
+      }
+     ],
+     "prompt_number": 37
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "#Add in pdb.set_trace()\n",
+      "import pdb\n",
+      "def calc_mean(ilist):\n",
+      "    total = 0\n",
+      "    for num in ilist:\n",
+      "        total = total + num\n",
+      "    pdb.set_trace()\n",
+      "    return(total/len(ilist))\n",
+      "\n",
+      "test_calc_mean(ilist)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 34
+    },
+    {
+     "cell_type": "raw",
+     "metadata": {},
+     "source": [
+      "When pdb stops execution type:\n",
+      ">>>print total\n",
+      ">>>print len(ilist)\n",
+      ">>> print total/len(ilist)\n",
+      ">>>print type(total)\n",
+      ">>>print type(ilist)\n",
+      "\n",
+      "Ah ha, the problem is that they are both integers"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file
diff --git a/unit_testing/traceback_example.py b/unit_testing/traceback_example.py
new file mode 100644 (file)
index 0000000..f1b12c5
--- /dev/null
@@ -0,0 +1,29 @@
+import math
+import sys
+
+def area_of_a_circle(radius):
+    '''
+    Calculate the area of a circle
+    '''
+    return 2.0 * math.pi * radius
+
+def area_of_a_square(half_side):
+    '''
+    Calculate the area of a square
+    '''
+    return (2.0*half_side) **2
+
+def diff_area_circle_area_square(radius):
+    '''
+    Calcualte how much bigger the area of a square is than
+    the area of a circle where the radius of the circle is
+    half the length of one side of the square.
+    '''
+    area_circ = area_of_a_circle(radius)
+    area_sq = area_of_a_square(radius)
+
+    return area_sq - area_circ
+
+if __name__ == "__main__":
+    r = sys.argv[1]
+    diff_area_circle_area_square(r)