Creating doctests with iPython
doctests are a great means of testing because they can better describe the code they are testing, with examples and comments in a very natural way for programmers.
However, Python editors are short of support for the crucial task of writing them; that is to say we lack auto-complete, Python syntax help, access to methods' docstrings, and so on. As a sad result, writing doctests is a pain in the neck.
Fortunately, iPython comes to the rescue with a special (and very nice, indeed) doctest compatibility mode: %doctest_mode
.
In [1]: %doctest_mode
*** Pasting of code with ">>>" or "..." has been enabled.
Exception reporting mode: Plain
Doctest mode is: ON
>>>
When entering in doctest_mode, the regular iPython prompts changes into the usual Python >>>
sign.
>>> i = 1 >>> i 1 >>> i += 1 >>> i 2 >>> a = (1,2,3,) >>> a (1, 2, 3)
After finishing the test, we can go back to the iPython shell via the %doctest_mode
command again:
>>> %doctest_mode Exception reporting mode: Context Doctest mode is: OFF In [10]:
Now that we know how to write doctests with iPython, let's create one.
Getting ready
First of all, we need an embedded iPython inside the test suite. This will let us use it, enter in doctest_mode, and then copy and paste all we need.
Edit tests.py
in the Products.poxContentTypes
package and add the following block of code:
import sys from IPython.Shell import IPShellEmbed def ipython(locals=None): """Provides an interactive shell aka console inside your testcase. It looks exactly like in a doctestcase and you can copy and paste code from the shell into your doctest. The locals in the testcase are available, because you are in the testcase. In your testcase or doctest you can invoke the shell at any point by calling:: >>> from Products.poxContentTypes.tests import ipython >>> ipython( locals() ) locals -- passed to InteractiveInterpreter.__init__() """ savestdout = sys.stdout sys.stdout = sys.stderr sys.stderr.write('='*70) embedshell = IPShellEmbed(argv=[], banner=""" IPython Interactive Console Note: You have the same locals available as in your test-case. """, exit_msg="""end of ZopeTestCase Interactive Console session""", user_ns=locals) embedshell() sys.stdout.write('='*70+'\n') sys.stdout = savestdout
How to do it...
Once the ipython
method is available, we'll call it from any of the test cases we want to work with (unit or integration test):
- Open the
INTEGRATION.txt
file in theProducts.poxContentTypes
package and add these lines at the beginning:>>> from Products.poxContentTypes.tests import ipython >>> ipython(locals())
- Make sure the following lines are uncommented in
tests
module:ztc.ZopeDocFileSuite( 'INTEGRATION.txt', package='Products.poxContentTypes', test_class=TestCase),
- Run the new test with this command:
./bin/instance test -s Products.poxContentTypes
- You'll get an iPython interpreter session in the console. Enter in
doctest_mode
by running:In [1]: %doctest_mode *** Pasting of code with ">>>" or "..." has been enabled. Exception reporting mode: Plain Doctest mode is: ON >>>
This will leave you in a fresh Plone site with new ZODB just as if you were running a test (we are indeed).
- After we have finished writing tests, we can exit
doctest_mode
(or not), copy the iPython session into the test file, and finally exit the iPython shell with%exit
command.... >>> %doctest_mode Exception reporting mode: Context Doctest mode is: OFF In [4]: %exit
The test suite we were running (and paused to write the actual testing routine) will finish with a success status.
In the following screenshot, you can see how to use iPython's %doctest_mode
to write an integration test: