Plone 3 Products Development Cookbook
上QQ阅读APP看书,第一时间看更新

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]: 

Tip

iPython magic commands begin with % and provide special functionalities not available in a regular Python shell. For more information about them, type %magic in your iPython interpreter.

Now that we know how to write doctests with iPython, let's create one.

Note

Please note that we are about to hack a regular test suite to let us work more comfortably while writing doctests. By no means is this something you should leave in a final package but should be used just for debugging or development purposes.

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.

Note

From now on we will continue working with paster-generated test suites. You should slightly adjust these steps if you prefer to work with the ArchGenXML-created test suite.

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

Note

This ipython method is inspired by the interact method created by ArchGenXML .

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):

  1. Open the INTEGRATION.txt file in the Products.poxContentTypes package and add these lines at the beginning:
    >>> from Products.poxContentTypes.tests import ipython
    >>> ipython(locals())

    Note

    The first import line must point to the module where we have added the ipython method.

  2. Make sure the following lines are uncommented in tests module:
            ztc.ZopeDocFileSuite(
                'INTEGRATION.txt', package='Products.poxContentTypes',
                test_class=TestCase),
  3. Run the new test with this command:
    ./bin/instance test -s Products.poxContentTypes
    
  4. 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).

  5. 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:

Note

You can also use iPython to write unit tests in which case you won't be able to work with a Plone site but just with your (or others') Python packages.

How to do it...