Testing and Debugging in Grok 1.0: Part 2

Exclusive offer: get 50% off this eBook here
Grok 1.0 Web Development

Grok 1.0 Web Development — Save 50%

Create flexible, agile web applications using the power of Grok—a Python web framework

$23.99    $12.00
by Carlos de la Guardia | February 2010 | Open Source Web Development

Read Part One of Testing and Debugging in Grok 1.0 here.

Adding unit tests

Apart from functional tests, we can also create pure Python test cases which the test runner can find. While functional tests cover application behavior, unit tests focus on program correctness. Ideally, every single Python method in the application should be tested.

The unit test layer does not load the Grok infrastructure, so tests should not take anything that comes with it for granted; just the basic Python behavior.

To add our unit tests, we'll create a module named unit_tests.py. Remember, in order for the test runner to find our test modules, their names have to end with 'tests'. Here's what we will put in this file:

""" 
Do a Python test on the app.

:unittest:
"""

import unittest
from todo.app import Todo

class InitializationTest(unittest.TestCase):
todoapp = None

def setUp(self):
self.todoapp = Todo()

def test_title_set(self):
self.assertEqual(self.todoapp.title,u'To-do list manager')

def test_next_id_set(self):
self.assertEqual(self.todoapp.next_id,0)

The :unittest: comment at the top, is very important. Without it, the test runner will not know in which layer your tests should be executed, and will simply ignore them.

Unit tests are composed of test cases, and in theory, each should contain several related tests based on a specific area of the application's functionality. The test cases use the TestCase class from the Python unittest module. In these tests, we define a single test case that contains two very simple tests.

We are not getting into the details here. Just notice that the test case can include a setUp and a tearDown method that can be used to perform any common initialization and destruction tasks which are needed to get the tests working and finishing cleanly.

Every test inside a test case needs to have the prefix 'test' in its name, so we have exactly two tests that fulfill this condition. Both of the tests need an instance of the Todo class to be executed, so we assign it as a class variable to the test case, and create it inside the setUp method. The tests are very simple and they just verify that the default property values are set on instance creation.

Both of the tests use the assertEqual method to tell the test runner that if the two values passed are different, the test should fail. To see them in action, we just run the bin/test command once more:

$ bin/test
Running tests at level 1
Running todo.FunctionalLayer tests:
Set up
in 2.691 seconds.
Running:
.......2009-09-30 22:00:50,703 INFO sqlalchemy.engine.base.
Engine.0x...684c PRAGMA table_info("users")

2009-09-30 22:00:50,703 INFO sqlalchemy.engine.base.Engine.0x...684c ()

Ran 7 tests with 0 failures and 0 errors in 0.420 seconds.
Running zope.testing.testrunner.layer.UnitTests tests:
Tear down todo.FunctionalLayer ... not supported
Running in a subprocess.
Set up zope.testing.testrunner.layer.UnitTests in 0.000 seconds.
Ran 2 tests with 0 failures and 0 errors in 0.000 seconds.
Tear down zope.testing.testrunner.layer.UnitTests in 0.000 seconds.
Total: 9 tests, 0 failures, 0 errors in 5.795 seconds

Now, both the functional and unit test layers contain some tests and both are run one after the other. We can see the subtotal for each layer at the end of its tests as well as the grand total of the nine passed tests when the test runner finishes its work.

Extending the test suite

Of course, we just scratched the surface of which tests should be added to our application. If we continue to add tests, hundreds of tests may be there by the time we finish. However, this article is not the place to do so.

As mentioned earlier, its way easier to have tests for each part of our application, if we add them as we code. There's no hiding from the fact that testing is a lot of work, but there is great value in having a complete test suite for our applications. More so, when third parties might use our work product independently.

Debugging

We will now take a quick look at the debugging facilities offered by Grok. Even if we have a very thorough test suite, chances are there that we will find a fair number of bugs in our application. When that happens, we need a quick and effective way to inspect the code as it runs and find the problem spots easily.

Often, developers will use print statements (placed at key lines) throughout the code, in the hopes of finding the problem spot. While this is usually a good way to begin locating sore spots in the code, we often need some way to follow the code line by line to really find out what's wrong. In the next section, we'll see how to use the Python debugger to step through the code and find the problem spots. We'll also take a quick look at how to do post-mortem debugging in Grok, which means jumping into the debugger to analyze program state immediately after an exception has occurred.

Debugging in Grok

For regular debugging, where we need to step through the code to see what's going on inside, the Python debugger is an excellent tool. To use it, you just have to add the next line at the point where you wish to start debugging:

import pdb; pdb.set_trace()

Let's try it out. Open the app.py module and change the add method of the AddProjectForm class (line 108) to look like this:

@grok.action('Add project') 
def add(self,**data):
import pdb; pdb.set_trace()
project = Project()
project.creator = self.request.principal.title
project.creation_date = datetime.datetime.now()
project.modification_date = datetime.datetime.now()
self.applyData(project,**data)
id = str(self.context.next_id)
self.context.next_id = self.context.next_id+1
self.context[id] = project
return self.redirect(self.url(self.context[id]))

Notice that we invoke the debugger at the beginning of the method. Now, start the instance, go to the 'add project' form, fill it up, and submit it. Instead of seeing the new project view, the browser will stay at the 'add form' page, and display the waiting for... message. This is because control has been transferred to the console for the debugger to act. Your console will look like this:


> /home/cguardia/work/virtual/grok1/todo/src/todo/app.py(109)add()
-> project = Project()
(Pdb) |

The debugger is now active and waiting for input. Notice that the line number where debugging started, appears right beside the path of the module where we are located. After the line number, comes the name of the method, add(). Below that, the next line of code to be executed is shown.

The debugger commands are simple. To execute the current line, type n:

(Pdb) n 
> /home/cguardia/work/virtual/grok1/todo/src/todo/app.py(110)add()
-> project.creator = self.request.principal.title
(Pdb)

You can see the available commands if you type h:

(Pdb) h 

Documented commands (type help <topic>):
========================================
EOF break condition disable help list q step w
a bt cont down ignore n quit tbreak whatis
alias c continue enable j next r u where
args cl d exit jump p return unalias
b clear debug h l pp s up

Miscellaneous help topics:
==========================
exec pdb

Undocumented commands:
======================
retval rv

(Pdb)

The list command id is used for getting a bird's eye view of where in the code are we:

(Pdb) list 
105
106 @grok.action('Add project')
107 def add(self,**data):
108 import pdb; pdb.set_trace()
109 project = Project()
110 -> project.creator = self.request.principal.title
111 project.creation_date = datetime.datetime.now()
112 project.modification_date = datetime.datetime.now()
113 self.applyData(project,**data)
114 id = str(self.context.next_id)
115 self.context.next_id = self.context.next_id+1
(Pdb)

As you can see, the current line is shown with an arrow.

It's possible to type in the names of objects within the current execution context and find out their values:

(Pdb) project 
<todo.app.Project object at 0xa0ef72c>
(Pdb) data
{'kind': 'personal', 'description': u'Nothing', 'title':
u'Project about nothing'}

(Pdb)

We can of course, continue stepping line by line through all of the code in the application, including Grok's own code, checking values as we proceed. When we are through reviewing, we can click on c to return control to the browser. At this point, we will see the project view.

The Python debugger is very easy to use and it can be invaluable for finding obscure bugs in your code.

Grok 1.0 Web Development Create flexible, agile web applications using the power of Grok—a Python web framework
Published: February 2010
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

The default Ajax debugger

The other kind of debugging is known as post-mortem debugging. In the previous section, we stepped through the code at leisure while the application was in a stable and running condition. Many times, however, we can run into an error condition that stops the program and we need to analyze the program state at the time at which the error occurred. That's what post-mortem debugging is about.

We'll now intentionally introduce an error in our code. Remove the import pdb line from the add method and change the first line after that to the following:

project = Project(**data)

The Project class' __init__ method does not expect this argument, so a TypeError will be raised. Restart the instance and add a project. Instead of a view, a blank screen with the error message,A system error occurred, will be shown.

Recall that so far, we have been using the deploy.ini file to start the instance, by using the following command:

$ bin/paster serve parts/etc/deploy.ini

To run a post-mortem debugger session, we have to start our instance with the debug profile instead:

$ bin/paster serve parts/etc/debug.ini

Try to add a project again. Now, instead of the simple error message, a full traceback of the error will be shown on screen, as shown in the following screenshot:

Grok 1.0 Web Development

One nice thing about the traceback is that it is expandable. A click on the brackets to the left will show the lines of code around the line where the error occurred, while clicking on the plus sign to the right of each module and line message will show additional traceback information. Above this information you will also see a text box which can be used to evaluate expressions within the current context (refer to the next screenshot).

Grok 1.0 Web Development

Post-mortem debugging with the Python debugger

The Ajax debugger is great, but if you are really used to the Python debugger, you might wish to use that instead for your post-mortem analysis. No problem, Grok comes prepared for this.

For a quick test, edit the parts/etc/debug.ini file in the project and change the word ajax to pdb in the [filter-app:main] section. It should look like this when you are done:

[filter-app:main] 
# Change the last part from 'ajax' to 'pdb' for a post-mortem debugger
# on the console:
use = egg:z3c.evalexception#pdb # <--- change here to pdb
next = zope


Now restart the instance with the debug profile and try to add a project. Instead of seeing the ajax screen, control will be transferred to the Python debugger on the console.

Keep in mind that we just modified a file that will be rewritten when we run the buildout again, so do this only for quick tests like we just did and never depend on changes to the files in the parts/etc directory. For making this change permanent, remember to edit the etc/debug.ini.in file instead of the one in parts/etc. You will then need to run the buildout again.

Summary

This article discussed how to test Grok applications and why it's important. We also covered debugging and presented some useful debugging tools for the Grok developer. Now that we have added tests, in the following article, we'll see how to deploy our application.

[ 1 | 2 ]

If you have read this article you may be interested to view :

Grok 1.0 Web Development Create flexible, agile web applications using the power of Grok—a Python web framework
Published: February 2010
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

About the Author :


Carlos de la Guardia

Carlos has been doing web consulting and development since 1994, when selling any kind of project required two meetings just to explain what the Internet was in the first place. He was the co-founder of Aldea Systems, a web consulting company where he spent ten years working in all kinds of web projects using a diverse range of languages and tools. In 2005 he became an independent developer and consultant, specializing in Zope and Plone projects. He frequently blogs about Plone and other Zope-related subjects.

Books From Packt

Joomla! 1.5 Multimedia
Joomla! 1.5 Multimedia

TYPO3 4.3 Multimedia Cookbook
TYPO3 4.3 Multimedia Cookbook

jQuery 1.4 Reference Guide
jQuery 1.4 Reference Guide

Python Testing: Beginner's Guide
Python Testing: Beginner's Guide

Backbase 4 RIA Development
Backbase 4 RIA Development

AJAX and PHP: Building Modern Web Applications 2nd Edition
AJAX and PHP: Building Modern Web Applications 2nd Edition

jQuery UI 1.7: The User Interface Library for jQuery
    jQuery UI 1.7: The User Interface Library for jQuery

CodeIgniter 1.7
CodeIgniter 1.7

Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software