{ "metadata": { "name": "02-modularization-documentation" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "#Testing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are different levels of testing. \n", "\n", "**Single Use Code**\n", "Sometime we write codes for one time data analysis. In this case we know exactly what is going in and what we expect to get out. We do sanity checks, we run code on something that we know the expected output and that is about it.\n", "\n", "**Repetative Use Code**\n", "When you get good at functional programming you start to use the same function over and over. In this case it is good to write a set of tests which you can run to make sure your function behaves the way you expect it in all situations - not just the first situation you wrote it for.\n", "\n", "**Test Driven Development**\n", "In large projects or contract driven projects a project can be defined by its tests (e.g. The program should do x when given y). In this case you can write the tests first and then when you program satisfies all of them, you are done." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###Testing the product\n", "\n", "The simplest testing you can do is to test that you get what you expect in a specific circumstance. Let's use the module we wrote yesterday" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import plot_temperature" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 1 }, { "cell_type": "code", "collapsed": false, "input": [ "dir(plot_temperature)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 2, "text": [ "['__builtins__',\n", " '__doc__',\n", " '__file__',\n", " '__name__',\n", " '__package__',\n", " 'convert_fahrenheit_to_celsius',\n", " 'np',\n", " 'plot_data',\n", " 'pyplot',\n", " 'read_csv_file']" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will start by testing read_csv_file" ] }, { "cell_type": "code", "collapsed": false, "input": [ "cat mosquito_data_A1.csv" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "year,temperature,rainfall,mosquitos\r\n", "2001,87,222,198\r\n", "2002,72,103,105\r\n", "2003,77,176,166\r\n", "2004,89,236,210\r\n", "2005,88,283,242\r\n", "2006,89,151,147\r\n", "2007,71,121,117\r\n", "2008,88,267,232\r\n", "2009,85,211,191\r\n", "2010,75,101,106\r\n" ] } ], "prompt_number": 3 }, { "cell_type": "code", "collapsed": false, "input": [ "def test_output_read_csv_file():\n", " year, temperature, rainfall, mosquitos = plot_temperature.read_csv_file('mosquito_data_A1.csv')\n", " year_key = [2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010]\n", " for yr_guess, yr_solution in zip(year, year_key):\n", " assert yr_guess == yr_solution, 'year doesn\\'t match key'" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are two new statements in here:\n", "\n", "1. zip: this says loop over each array together (e.g. take the first item of every array, then the second, then the third, etc)\n", "2. assert: assert statements have the format: assert conditional, print_statement. This says if this statement is not true, print this message" ] }, { "cell_type": "code", "collapsed": false, "input": [ "test_output_read_csv_file()" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 5 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternately you can type:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def test_output_read_csv_file():\n", " year, temperature, rainfall, mosquitos = plot_temperature.read_csv_file('mosquito_data_A1.csv')\n", " assert (year == np.array([2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010])).all()" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 9 }, { "cell_type": "code", "collapsed": false, "input": [ "test_output_read_csv_file()" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 10 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This test actually tests that given known input we get out what we expect." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###Challenge\n", "Write a test for convert_fahrenheit_to_celsius" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###Solution" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def test_convert_fahrenheit_to_celsius():\n", " temp_c = plot_temperature.convert_fahrenheit_to_celsius(32)\n", " assert temp_c == 0, '32F != 0C'" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 13 }, { "cell_type": "code", "collapsed": false, "input": [ "test_convert_fahrenheit_to_celsius()" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 14 }, { "cell_type": "markdown", "metadata": {}, "source": [ "###Testing inputs\n", "Sometimes you may want to test that a user gave you the right input. For instance in read_csv_file you may want to test that what is passed in is a string. This often needs to be done in the code." ] }, { "cell_type": "code", "collapsed": false, "input": [ "def read_csv_file(filename):\n", " '''\n", " This code will read in a CSV file of year, temperature, rainfall, and number of mosquitos and return 4 arrays, one for each column\n", " '''\n", " assert type(filename) is str, 'filename must be a string'\n", " year, temperature, rainfall, mosquitos = np.genfromtxt(filename, skiprows = 1, delimiter = ',', unpack = True)\n", " return year, temperature, rainfall, mosquitos" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 22 }, { "cell_type": "code", "collapsed": false, "input": [ "read_csv_file('mosquito_data_A1.csv')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 24, "text": [ "(array([ 2001., 2002., 2003., 2004., 2005., 2006., 2007., 2008.,\n", " 2009., 2010.]),\n", " array([ 87., 72., 77., 89., 88., 89., 71., 88., 85., 75.]),\n", " array([ 222., 103., 176., 236., 283., 151., 121., 267., 211., 101.]),\n", " array([ 198., 105., 166., 210., 242., 147., 117., 232., 191., 106.]))" ] } ], "prompt_number": 24 }, { "cell_type": "code", "collapsed": false, "input": [ "def read_csv_file(filename):\n", " '''\n", " This code will read in a CSV file of year, temperature, rainfall, and number of mosquitos and return 4 arrays, one for each column\n", " '''\n", " assert isinstance(filename, str), 'filename must be a string'\n", " year, temperature, rainfall, mosquitos = np.genfromtxt(filename, skiprows = 1, delimiter = ',', unpack = True)\n", " return year, temperature, rainfall, mosquitos" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 25 }, { "cell_type": "code", "collapsed": false, "input": [ "read_csv_file('mosquito_data_A1.csv')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 26, "text": [ "(array([ 2001., 2002., 2003., 2004., 2005., 2006., 2007., 2008.,\n", " 2009., 2010.]),\n", " array([ 87., 72., 77., 89., 88., 89., 71., 88., 85., 75.]),\n", " array([ 222., 103., 176., 236., 283., 151., 121., 267., 211., 101.]),\n", " array([ 198., 105., 166., 210., 242., 147., 117., 232., 191., 106.]))" ] } ], "prompt_number": 26 }, { "cell_type": "markdown", "metadata": {}, "source": [ "What if our statement is not true" ] }, { "cell_type": "code", "collapsed": false, "input": [ "read_csv_file(4)" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "AssertionError", "evalue": "filename must be a string", "output_type": "pyerr", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mread_csv_file\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[0m", "\u001b[0;32m\u001b[0m in \u001b[0;36mread_csv_file\u001b[0;34m(filename)\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mThis\u001b[0m \u001b[0mcode\u001b[0m \u001b[0mwill\u001b[0m \u001b[0mread\u001b[0m \u001b[0;32min\u001b[0m \u001b[0ma\u001b[0m \u001b[0mCSV\u001b[0m \u001b[0mfile\u001b[0m \u001b[0mof\u001b[0m \u001b[0myear\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtemperature\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrainfall\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mnumber\u001b[0m \u001b[0mof\u001b[0m \u001b[0mmosquitos\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;36m4\u001b[0m \u001b[0marrays\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mone\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0meach\u001b[0m \u001b[0mcolumn\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m '''\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilename\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'filename must be a string'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6\u001b[0m \u001b[0myear\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtemperature\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrainfall\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmosquitos\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgenfromtxt\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilename\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mskiprows\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdelimiter\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m','\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0munpack\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0myear\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtemperature\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrainfall\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmosquitos\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mAssertionError\u001b[0m: filename must be a string" ] } ], "prompt_number": 27 }, { "cell_type": "markdown", "metadata": {}, "source": [ "###Challenge\n", "write a function which tests the input of convert_fahrenheit_to_celsius()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###Solution" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def convert_fahrenheit_to_celsius(temp_in_f):\n", " '''\n", " This code will convert an array of tempertures from fahrenheit to celsius\n", " '''\n", " assert isinstance(temp_in_f, float) or isinstance(temp_in_f, int), 'temperature must be an int or float'\n", " temp_in_c = (temp_in_f - 32) * 5 / 9.0\n", " return temp_in_c" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 29 }, { "cell_type": "code", "collapsed": false, "input": [ "convert_fahrenheit_to_celsius(4)\n", "convert_fahrenheit_to_celsius(10.)\n", "convert_fahrenheit_to_celsius('a')" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "AssertionError", "evalue": "temperature must be an int or float", "output_type": "pyerr", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mconvert_fahrenheit_to_celsius\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[0mconvert_fahrenheit_to_celsius\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m10.\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mconvert_fahrenheit_to_celsius\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;32m\u001b[0m in \u001b[0;36mconvert_fahrenheit_to_celsius\u001b[0;34m(temp_in_f)\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mThis\u001b[0m \u001b[0mcode\u001b[0m \u001b[0mwill\u001b[0m \u001b[0mconvert\u001b[0m \u001b[0man\u001b[0m \u001b[0marray\u001b[0m \u001b[0mof\u001b[0m \u001b[0mtempertures\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mfahrenheit\u001b[0m \u001b[0mto\u001b[0m \u001b[0mcelsius\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m '''\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtemp_in_f\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfloat\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtemp_in_f\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'temperature must be an int or float'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6\u001b[0m \u001b[0mtemp_in_c\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mtemp_in_f\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m32\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0;36m5\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m9.0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mtemp_in_c\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mAssertionError\u001b[0m: temperature must be an int or float" ] } ], "prompt_number": 32 }, { "cell_type": "markdown", "metadata": {}, "source": [ "#Move out of the notebook\n", "1. Copy and paste updated versions of read_csv_file and convert_fahrenheit_to_celsius into file and save\n", "2. Copy and paste test_... functions into a file and save it as test_plot_temperature.py\n", "3. type nosetests" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [] } ], "metadata": {} } ] }