{ "metadata": { "name": "testing_in_ipynb" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Testing inside an IPython Notebook\n", "===================================\n", "\n", "In my courses and as part of [Software Carpentry](http://software-carpentry.org)\n", "I'm teaching more and more in IPython notebooks. One of the things I/we teach\n", "is testing, so I wondered if I could teach it in a notebook as well. With quite\n", "a lot of help from [Matthias Bussonnier](https://twitter.com/Mbussonn) I got it\n", "working so I thought I'd share." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "An example\n", "----------------\n", "\n", "We will test a function that caluculates the GC-content of a DNA sequence.\n", "The GC-content is simply the percentage of bases in the DNA sequence that\n", "are either G's or C's. So, for example, the GC-content of 'ATTGC' is 40%.\n", "\n", "The function we are testing is ``get_gc_content()`` and it takes a single\n", "argument, which is a string represting a sequence. This function is in\n", "a custom module called ``dna_analysis.py``.\n", "\n", "### Create the module to test\n", "We can use the ``%%file`` magic to save a block of code to a file, so we\n", "can use that to create the module that we're going to test." ] }, { "cell_type": "code", "collapsed": false, "input": [ "%%file dna_analysis.py\n", "\n", "\"\"\"Code for analyzing DNA sequences\"\"\"\n", "\n", "from __future__ import division\n", "\n", "def get_gc_content(seq):\n", " \"\"\"Determine the GC content of a sequence\"\"\"\n", " seq = seq.upper()\n", " gc_content = 100 * (seq.count('G') + seq.count('C')) / len(seq)\n", " return gc_content" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Writing dna_analysis.py\n" ] } ], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create the test module\n", "1. To use nosetests we need to save the test code to a Python file starting with the word *test*\n", "2. We then need to import the function(s) we are going to test\n", "3. Then we right a simple function (whose name starts with *test*) that calls the function and\n", "checks to see if it returns the right value" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%%file test_dna.py\n", "\n", "from dna_analysis import get_gc_content\n", "\n", "def test_get_gc_content_zero():\n", " assert get_gc_content('ATTATTAAA') == 0\n", " \n", "def test_get_gc_content_lowercase():\n", " assert get_gc_content('atgcatgc') == 50\n", " \n", "def test_get_gc_content_multiline():\n", " sequence = \"\"\"atta\n", "gccg\n", "attt\n", "cccg\"\"\"\n", " assert get_gc_content(sequence) == 50" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Writing test_dna.py\n" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Run nosetests\n", "We would normally run nosetests from the command line, so in IPython we can just call ``!nosetests``." ] }, { "cell_type": "code", "collapsed": false, "input": [ "!nosetests" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "..F\r\n", "======================================================================\r\n", "FAIL: test_dna.test_get_gc_content_multiline\r\n", "----------------------------------------------------------------------\r\n", "Traceback (most recent call last):\r\n", " File \"/usr/lib/python2.7/dist-packages/nose/case.py\", line 197, in runTest\r\n", " self.test(*self.arg)\r\n", " File \"/home/ethan/Dropbox/Teaching/ProgBiol/repo/ipynbs/test_dna.py\", line 15, in test_get_gc_content_multiline\r\n", " assert get_gc_content(sequence) == 50\r\n", "AssertionError\r\n", "\r\n", "----------------------------------------------------------------------\r\n", "Ran 3 tests in 0.018s\r\n", "\r\n", "FAILED (failures=1)\r\n" ] } ], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is exactly we output we want since the original function handles most basic cases,\n", "but not multiline strings." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Other kinds of testing\n", "----------------------\n", "There is also a nice example of running [doctests in the notebook](http://catherinedevlin.blogspot.co.uk/2012/10/im-increasingly-amazed-at-ipython.html)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Value?\n", "------\n", "I wonder a bit about how valuable this is in the sense that we probably wouldn't\n", "normally run tests this way (at least I don't at the moment). But, I think at\n", "least for a short workshop where we're teaching all of the Python in a notebook\n", "that the value of reducing the cognitive load relative to switching environments\n", "for the testing portion might outweigh doing something in a way that might not be exactly\n", "how we would do it in day to day work." ] } ], "metadata": {} } ] }