From: Jon Speicher Date: Sat, 27 Jul 2013 19:31:16 +0000 (-0400) Subject: Add the start of TDD and the exercise X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=bfffb8c1a7b56bc7afd0fc7fa3c0e7c6011348d9;p=swc-testing-nose.git Add the start of TDD and the exercise --- diff --git a/python/sw_engineering/SoftwareEngineering.ipynb b/python/sw_engineering/SoftwareEngineering.ipynb index ae0c7e6..78df33a 100644 --- a/python/sw_engineering/SoftwareEngineering.ipynb +++ b/python/sw_engineering/SoftwareEngineering.ipynb @@ -479,7 +479,7 @@ "language": "python", "metadata": {}, "outputs": [], - "prompt_number": 11 + "prompt_number": 12 }, { "cell_type": "markdown", @@ -509,13 +509,13 @@ "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 5\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0;36m4\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0madd_two_plus_two\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"2 + 2 didn't equal 4\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0mtest_add_two_plus_two_equals_four\u001b[0m\u001b[0;34m(\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;36mtest_add_two_plus_two_equals_four\u001b[0;34m()\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mtest_add_two_plus_two_equals_four\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0;36m4\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0madd_two_plus_two\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"2 + 2 didn't equal 4\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0mtest_add_two_plus_two_equals_four\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0;36m4\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0madd_two_plus_two\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"2 + 2 didn't equal 4\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0mtest_add_two_plus_two_equals_four\u001b[0m\u001b[0;34m(\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;36mtest_add_two_plus_two_equals_four\u001b[0;34m()\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mtest_add_two_plus_two_equals_four\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0;36m4\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0madd_two_plus_two\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"2 + 2 didn't equal 4\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0mtest_add_two_plus_two_equals_four\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mAssertionError\u001b[0m: 2 + 2 didn't equal 4" ] } ], - "prompt_number": 12 + "prompt_number": 13 }, { "cell_type": "markdown", @@ -621,7 +621,7 @@ "language": "python", "metadata": {}, "outputs": [], - "prompt_number": 13 + "prompt_number": 14 }, { "cell_type": "code", @@ -634,31 +634,31 @@ "outputs": [ { "html": [ - "
" + "
" ], "output_type": "display_data" }, { "javascript": [ - "document.ipython_nose_bfec549d740f4b038703369ae3f06e87 = $(\"#ipython_nose_bfec549d740f4b038703369ae3f06e87\");" + "document.ipython_nose_d38e10d9d55741bd9baa798e352d80ea = $(\"#ipython_nose_d38e10d9d55741bd9baa798e352d80ea\");" ], "output_type": "display_data" }, { "javascript": [ - "document.ipython_nose_bfec549d740f4b038703369ae3f06e87.append($(\".\"));" + "document.ipython_nose_d38e10d9d55741bd9baa798e352d80ea.append($(\".\"));" ], "output_type": "display_data" }, { "javascript": [ - "document.ipython_nose_bfec549d740f4b038703369ae3f06e87.append($(\"F\"));" + "document.ipython_nose_d38e10d9d55741bd9baa798e352d80ea.append($(\"F\"));" ], "output_type": "display_data" }, { "javascript": [ - "delete document.ipython_nose_bfec549d740f4b038703369ae3f06e87;" + "delete document.ipython_nose_d38e10d9d55741bd9baa798e352d80ea;" ], "output_type": "display_data" }, @@ -782,9 +782,9 @@ " testMethod()\n", " File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n", " self.test(*self.arg)\n", - " File \"<ipython-input-12-2d797333ff4c>\", line 5, in test_add_two_plus_two_equals_four\n", " assert 4 == add_two_plus_two(), \"2 + 2 didn't equal 4\"\n", "AssertionError: 2 + 2 didn't equal 4\n", @@ -793,7 +793,7 @@ " " ], "output_type": "pyout", - "prompt_number": 14, + "prompt_number": 15, "text": [ "1/2 tests passed; 1 failed\n", "========\n", @@ -804,14 +804,14 @@ " testMethod()\n", " File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n", " self.test(*self.arg)\n", - " File \"\", line 5, in test_add_two_plus_two_equals_four\n", + " File \"\", line 5, in test_add_two_plus_two_equals_four\n", " assert 4 == add_two_plus_two(), \"2 + 2 didn't equal 4\"\n", "AssertionError: 2 + 2 didn't equal 4\n", "\n" ] } ], - "prompt_number": 14 + "prompt_number": 15 }, { "cell_type": "markdown", @@ -835,13 +835,13 @@ "text": [ "........\r\n", "----------------------------------------------------------------------\r\n", - "Ran 8 tests in 0.003s\r\n", + "Ran 8 tests in 0.004s\r\n", "\r\n", "OK\r\n" ] } ], - "prompt_number": 15 + "prompt_number": 16 }, { "cell_type": "markdown", @@ -883,31 +883,31 @@ "outputs": [ { "html": [ - "
" + "
" ], "output_type": "display_data" }, { "javascript": [ - "document.ipython_nose_7affb7b5cb134255ac605e091f8bd433 = $(\"#ipython_nose_7affb7b5cb134255ac605e091f8bd433\");" + "document.ipython_nose_b902caf7914c4fb3b2efaf1c540ce8e0 = $(\"#ipython_nose_b902caf7914c4fb3b2efaf1c540ce8e0\");" ], "output_type": "display_data" }, { "javascript": [ - "document.ipython_nose_7affb7b5cb134255ac605e091f8bd433.append($(\".\"));" + "document.ipython_nose_b902caf7914c4fb3b2efaf1c540ce8e0.append($(\".\"));" ], "output_type": "display_data" }, { "javascript": [ - "document.ipython_nose_7affb7b5cb134255ac605e091f8bd433.append($(\".\"));" + "document.ipython_nose_b902caf7914c4fb3b2efaf1c540ce8e0.append($(\".\"));" ], "output_type": "display_data" }, { "javascript": [ - "delete document.ipython_nose_7affb7b5cb134255ac605e091f8bd433;" + "delete document.ipython_nose_b902caf7914c4fb3b2efaf1c540ce8e0;" ], "output_type": "display_data" }, @@ -1023,13 +1023,13 @@ " " ], "output_type": "pyout", - "prompt_number": 17, + "prompt_number": 18, "text": [ "2/2 tests passed\n" ] } ], - "prompt_number": 17 + "prompt_number": 18 }, { "cell_type": "markdown", @@ -1038,37 +1038,903 @@ "***" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "# Test-driven development\n", + "***\n", + "\n", + "We've seen how to refactor code, and we've seen how to write and run tests. We have code to read an animals file and to spit out the data. Next we need code to find the mean of a bunch of numbers, and we'll need code to filter the list of data to pull out only the animals that we want to count. We'll start by writing the `mean` function, and we'll start writing the `mean` function by writing a test.\n", + "\n", + "Why would we write the tests first? *Test-driven development (TDD)* is a fairly recent concept in software engineering. The idea is that you develop a particular piece of functionality in small, iterative chunks: write a test that fails, write some code to make the test pass, write another test that fails, write some more code to make the test pass, etcetera. This is believed to have the following benefits:\n", + "\n", + "* Test coverage is extremely high, because you won't write code unless prompted by a test.\n", + "* Code size is minimized, because you won't be tempted to \"throw in the kitchen sink\" when writing a function that strictly passes tests.\n", + "* Code complexity is reduced, because you can \"fearlessly refactor\" code if you have a battery of tests to prove that you haven't broken anything in the process.\n", + "* The tests serve as \"documentation\" for what a function does and does not do.\n", + "\n", + "The jury is still out on whether TDD is valuable enough to justify the extra ceremony, and some people consider it to be superfluous or trendy. The important thing is that your code is tested, whether the tests came first or not.\n", + "\n", + "Let's give it a try." + ] + }, { "cell_type": "code", "collapsed": false, "input": [ - "def calc_mean(ilist):\n", - " '''\n", - " returns the mean of input list\n", - " '''\n", - " if len(ilist) == 0:\n", - " raise ValueError('Input is empty.')\n", - " \n", - " total = 0.0\n", - " for num in ilist:\n", - " total = total + num\n", - " return total/float(len(ilist))" + "def test_mean_of_zero():\n", + " assert 0 == mean([0])" ], "language": "python", "metadata": {}, "outputs": [], - "prompt_number": 30 + "prompt_number": 19 }, { "cell_type": "code", "collapsed": false, "input": [ - "import nose.tools as nt" + "%nose" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "html": [ + "
" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "document.ipython_nose_a3c3d168eb2c4a988da0f4a492cbd197 = $(\"#ipython_nose_a3c3d168eb2c4a988da0f4a492cbd197\");" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "document.ipython_nose_a3c3d168eb2c4a988da0f4a492cbd197.append($(\"E\"));" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "document.ipython_nose_a3c3d168eb2c4a988da0f4a492cbd197.append($(\".\"));" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "document.ipython_nose_a3c3d168eb2c4a988da0f4a492cbd197.append($(\".\"));" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "delete document.ipython_nose_a3c3d168eb2c4a988da0f4a492cbd197;" + ], + "output_type": "display_data" + }, + { + "html": [ + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "  \n", + "
\n", + "
\n", + "  \n", + "
\n", + "
\n", + "  \n", + "
\n", + " 2/3 tests passed; 1 failed\n", + "
\n", + " \n", + "
\n", + "
\n", + " failed: __main__.test_mean_of_zero\n", + " [toggle traceback]\n", + "
\n", + "
Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"<ipython-input-19-481e1c58db22>\", line 2, in test_mean_of_zero\n",
+        "    assert 0 == mean([0])\n",
+        "NameError: global name 'mean' is not defined\n",
+        "
\n", + "
\n", + " " + ], + "output_type": "pyout", + "prompt_number": 20, + "text": [ + "2/3 tests passed; 1 failed\n", + "========\n", + "__main__.test_mean_of_zero\n", + "========\n", + "Traceback (most recent call last):\n", + " File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n", + " testMethod()\n", + " File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n", + " self.test(*self.arg)\n", + " File \"\", line 2, in test_mean_of_zero\n", + " assert 0 == mean([0])\n", + "NameError: global name 'mean' is not defined\n", + "\n" + ] + } + ], + "prompt_number": 20 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def mean():\n", + " pass" ], "language": "python", "metadata": {}, "outputs": [], - "prompt_number": 27 + "prompt_number": 21 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "%nose" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "html": [ + "
" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "document.ipython_nose_bbb38260de4a452086abb22e7db22dfd = $(\"#ipython_nose_bbb38260de4a452086abb22e7db22dfd\");" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "document.ipython_nose_bbb38260de4a452086abb22e7db22dfd.append($(\"E\"));" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "document.ipython_nose_bbb38260de4a452086abb22e7db22dfd.append($(\".\"));" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "document.ipython_nose_bbb38260de4a452086abb22e7db22dfd.append($(\".\"));" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "delete document.ipython_nose_bbb38260de4a452086abb22e7db22dfd;" + ], + "output_type": "display_data" + }, + { + "html": [ + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "  \n", + "
\n", + "
\n", + "  \n", + "
\n", + "
\n", + "  \n", + "
\n", + " 2/3 tests passed; 1 failed\n", + "
\n", + " \n", + "
\n", + "
\n", + " failed: __main__.test_mean_of_zero\n", + " [toggle traceback]\n", + "
\n", + "
Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"<ipython-input-19-481e1c58db22>\", line 2, in test_mean_of_zero\n",
+        "    assert 0 == mean([0])\n",
+        "TypeError: mean() takes no arguments (1 given)\n",
+        "
\n", + "
\n", + " " + ], + "output_type": "pyout", + "prompt_number": 22, + "text": [ + "2/3 tests passed; 1 failed\n", + "========\n", + "__main__.test_mean_of_zero\n", + "========\n", + "Traceback (most recent call last):\n", + " File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n", + " testMethod()\n", + " File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n", + " self.test(*self.arg)\n", + " File \"\", line 2, in test_mean_of_zero\n", + " assert 0 == mean([0])\n", + "TypeError: mean() takes no arguments (1 given)\n", + "\n" + ] + } + ], + "prompt_number": 22 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def mean(numbers):\n", + " pass" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 23 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "%nose" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "html": [ + "
" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "document.ipython_nose_183426a0661b43479529049b993ae958 = $(\"#ipython_nose_183426a0661b43479529049b993ae958\");" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "document.ipython_nose_183426a0661b43479529049b993ae958.append($(\"F\"));" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "document.ipython_nose_183426a0661b43479529049b993ae958.append($(\".\"));" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "document.ipython_nose_183426a0661b43479529049b993ae958.append($(\".\"));" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "delete document.ipython_nose_183426a0661b43479529049b993ae958;" + ], + "output_type": "display_data" + }, + { + "html": [ + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "  \n", + "
\n", + "
\n", + "  \n", + "
\n", + "
\n", + "  \n", + "
\n", + " 2/3 tests passed; 1 failed\n", + "
\n", + " \n", + "
\n", + "
\n", + " failed: __main__.test_mean_of_zero\n", + " [toggle traceback]\n", + "
\n", + "
Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"<ipython-input-19-481e1c58db22>\", line 2, in test_mean_of_zero\n",
+        "    assert 0 == mean([0])\n",
+        "AssertionError\n",
+        "
\n", + "
\n", + " " + ], + "output_type": "pyout", + "prompt_number": 24, + "text": [ + "2/3 tests passed; 1 failed\n", + "========\n", + "__main__.test_mean_of_zero\n", + "========\n", + "Traceback (most recent call last):\n", + " File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n", + " testMethod()\n", + " File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n", + " self.test(*self.arg)\n", + " File \"\", line 2, in test_mean_of_zero\n", + " assert 0 == mean([0])\n", + "AssertionError\n", + "\n" + ] + } + ], + "prompt_number": 24 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def mean(numbers):\n", + " return 0" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 25 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "%nose" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "html": [ + "
" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "document.ipython_nose_6748615aba7a4a3abecfdbad96ac3251 = $(\"#ipython_nose_6748615aba7a4a3abecfdbad96ac3251\");" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "document.ipython_nose_6748615aba7a4a3abecfdbad96ac3251.append($(\".\"));" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "document.ipython_nose_6748615aba7a4a3abecfdbad96ac3251.append($(\".\"));" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "document.ipython_nose_6748615aba7a4a3abecfdbad96ac3251.append($(\".\"));" + ], + "output_type": "display_data" + }, + { + "javascript": [ + "delete document.ipython_nose_6748615aba7a4a3abecfdbad96ac3251;" + ], + "output_type": "display_data" + }, + { + "html": [ + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "  \n", + "
\n", + "
\n", + "  \n", + "
\n", + "
\n", + "  \n", + "
\n", + " 3/3 tests passed\n", + "
\n", + " " + ], + "output_type": "pyout", + "prompt_number": 26, + "text": [ + "3/3 tests passed\n" + ] + } + ], + "prompt_number": 26 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When we follow this sequence, you can see how the tests *drive* the development of the `mean` function. You can also see that this can be tedious, especially for newbies." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise: Write the `mean` function and its tests " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Write a function called `mean` that accepts a list of numbers and returns the arithmetic mean. It should work like this:\n", + "\n", + " In [1]: mean([5, 7, 15, 9, 0, 18])\n", + " Out [1]: 9.0\n", + "\n", + "Make sure to write a suite of tests for your implementation - either first or after the fact, whichever seems more logical to you. Some ideas about what to test:\n", + "\n", + "* A list with one number\n", + "* A list of all zeros\n", + "* Integers with an integral mean\n", + "* Integers with a non-integral mean\n", + "* Floating point numbers\n", + "\n", + "TBD: empty list? exception?" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def calc_mean(ilist):\n", + " '''\n", + " returns the mean of input list\n", + " '''\n", + " if len(ilist) == 0:\n", + " raise ValueError('Input is empty.')\n", + " \n", + " total = 0.0\n", + " for num in ilist:\n", + " total = total + num\n", + " return total/float(len(ilist))" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 27 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "import nose.tools as nt" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "9.0\n", + "56\n" + ] + } + ], + "prompt_number": 31 }, { "cell_type": "code",