#!/usr/bin/env python # coding: utf-8 # > This is one of the 100 recipes of the [IPython Cookbook](http://ipython-books.github.io/), the definitive guide to high-performance scientific computing and data science in Python. # # # 2.7. Writing unit tests with nose # # **This is the Python 3 version of the recipe.** # # Although Python has a native unit testing module (unittest), we will rather use Nose which is more convenient and more powerful. Having an extra dependency is not a problem as the Nose package is only required to launch the test suite, and not to use the software itself. # ## Creation of the Python module # In[ ]: get_ipython().run_cell_magic('writefile', 'datautils.py', '# Version 1.\nimport os\nfrom urllib.request import urlopen # Python 2: use urllib2\n\ndef download(url):\n """Download a file and save it in the current folder.\n Return the name of the downloaded file."""\n # Get the filename.\n file = os.path.basename(url)\n # Download the file unless it already exists.\n if not os.path.exists(file):\n with open(file, \'w\') as f:\n f.write(urlopen(url).read())\n return file\n') # ## Creation of the test module # In[ ]: get_ipython().run_cell_magic('writefile', 'test_datautils.py', 'from urllib.request import (HTTPHandler, install_opener, \n build_opener, addinfourl)\nimport os\nimport shutil\nimport tempfile\nfrom io import StringIO # Python 2: use StringIO\nfrom datautils import download\n\nTEST_FOLDER = tempfile.mkdtemp()\nORIGINAL_FOLDER = os.getcwd()\n\nclass TestHTTPHandler(HTTPHandler):\n """Mock HTTP handler."""\n def http_open(self, req):\n resp = addinfourl(StringIO(\'test\'), \'\', req.get_full_url(), 200)\n resp.msg = \'OK\'\n return resp\n \ndef setup():\n """Install the mock HTTP handler for unit tests."""\n install_opener(build_opener(TestHTTPHandler))\n os.chdir(TEST_FOLDER)\n \ndef teardown():\n """Restore the normal HTTP handler."""\n install_opener(build_opener(HTTPHandler))\n # Go back to the original folder.\n os.chdir(ORIGINAL_FOLDER)\n # Delete the test folder.\n shutil.rmtree(TEST_FOLDER)\n\ndef test_download1():\n file = download("http://example.com/file.txt")\n # Check that the file has been downloaded.\n assert os.path.exists(file)\n # Check that the file contains the contents of the remote file.\n with open(file, \'r\') as f:\n contents = f.read()\n print(contents)\n assert contents == \'test\'\n') # ## Launching the tests # In[ ]: get_ipython().system('nosetests') # ## Adding a failing test # Now, let's add a new test. # In[ ]: get_ipython().run_cell_magic('writefile', 'test_datautils.py -a', '\ndef test_download2():\n file = download("http://example.com/")\n assert os.path.exists(file)\n') # In[ ]: get_ipython().system('nosetests') # ## Fixing the failing test # The new test fails because the filename cannot be inferred from the URL, so we need to handle this case. # In[ ]: get_ipython().run_cell_magic('writefile', 'datautils.py', '# Version 2.\nimport os\nfrom urllib.request import urlopen # Python 2: use urllib2\n\ndef download(url):\n """Download a file and save it in the current folder.\n Return the name of the downloaded file."""\n # Get the filename.\n file = os.path.basename(url)\n # Fix the bug, by specifying a fixed filename if the URL \n # does not contain one.\n if not file:\n file = \'downloaded\'\n # Download the file unless it already exists.\n if not os.path.exists(file):\n with open(file, \'w\') as f:\n f.write(urlopen(url).read())\n return file\n') # In[ ]: get_ipython().system('nosetests') # > You'll find all the explanations, figures, references, and much more in the book (to be released later this summer). # # > [IPython Cookbook](http://ipython-books.github.io/), by [Cyrille Rossant](http://cyrille.rossant.net), Packt Publishing, 2014 (500 pages).