Section 21.2. TurboGears testutil


21.2. TurboGears testutil

Nose is great, but TurboGears apps are complicated beasts, and it is sometimes not so trivial to test them properly. The testutil module of TurboGears provides lots of shortcuts and convenient classes and functions you can use. You will use the famous bookmarker as a test bed. If you recall in Chapter 4, "Creating a Simple Application," you already used testutil briefly to test your controllers and make sure they returned the proper data. The goal here is to make bookmarker a little more robust and to make sure that all bookmarks point to existing URLs (no 404 errors) and that there are no duplicate bookmarks (two bookmarks with the same name and/or same URL).

How do you test a TurboGears application? There are different test dimensions: you can test your DB to make sure it contains the right data; you can test your controller methods to make sure they return the right result; you can test your view to make sure it returns a properly formatted and styled page with the correct data; and you can test your Ajax code in the browsers (plural). All these tests are important; but, you usually don't have to write so many tests for every feature or bug fix. Usually you will have some generic mechanisms, like a Kid template, that know how to render a page given a list of records from your DB. Once you've tested this template on some record lists, you gain confidence that it works and you don't need to write additional view tests for new controller methods. It is enough to make sure that the new controller method returns a dictionary with the expected record list.

Let's start with the database and make sure that it doesn't allow you to store duplicate bookmarks. We'll start by getting the preliminaries out of the way. You need to import quite a few packages to conduct sophisticated tests. Besides your projects model and controllers.Root, you want TurboGears's testutil and database, as well as cherrypy.

By importing testutil you will get some enormous benefits. TurboGears creates a SQLite DB in memory. This is a wonderful feature of SQLite. You can't wreak havoc on your production DB, there is no need for admin rights, there aren't any permission issues or wrong filenames, and it's fast.

Of course, TurboGears supports you if you have to test with a different DB (e.g. your code uses nested transactions that SQLite doesn't support) or if you want to test on a real DB with lots of data. All you have to do is set_db_uri to point to whatever test database you want to use, and you are good to go.

You may also want to import the with_setup and with_teardown decorators from Nose. We use only with_setup in this code.

The next step is setting CherryPy's root to your Root controller. Once again, turbogears.testutil comes to the rescue and helps by setting a couple of useful configuration settings for you (new style logging, no autoreload) and starting the built-in CherryPy server so it will be ready for you.

import os, sys from turbogears import (testutil,                         database) from bookmarker.controllers import Root from bookmarker.model import (Bookmarks,                               Categories) import cherrypy from nose import with_setup cherrypy.root = Root()


It's time to define a test class. In the previous section, we showed how to write test functions. Nose supports test classes, too, and that's what we'll use now. The test class is called BookmarkTest and it subclasses testutil.DBTest. The base class provides fixture services setUp/tearDown to your test code. The setUp method is called before every test method and the tearDown method is called after each test. This allows great isolation of tests because you verify that every tests starts in pristine state. So, DBTest creates all your model's tables before every test and drops them after every test. It also rolls back your DB. I'm not sure how effective it is. If your code did something and committed it, the rollback won't help you. That is why it's a good idea to use the default memory database or another special testing database for your test class, rather than take the chance of corrupting your production database.

class BookmarkTest(testutil.DBTest):     ...


Before we actually write some tests, let's define a helper class. The entire test class deals with saving new bookmarks to the DB so the save_new_bookmark will come in handy. It accepts a dictionary of parameters that match the save_bookmark method in your controller, prepares a url, and calls the testutil.createRequest() function (possibly multiple times).

def save_new_bookmark(self, params, times=1):     pairs = ['%s=%s' % (k, params[k]) for k in params]     url = '/save_bookmark/0?' + '&'.join(pairs)     for i in range(times):         testutil.createRequest(url)


testutil provides two ways to invoke your controller: the call() method and the createRequest() method. One simple tip can save you a lot of grief: Never invoke a redirected controller method (like save_bookmark) using testutil.call(). testutil.call() uses a DummyRequest class that works fine if the controller just returns a dictionary; but if a redirection is called for, the DummyRequest can't deliver the dictionary when it is accessed during processing. This is the reason that the save_new_bookmark helper uses createRequest(), which creates a full fledged CherryPy request object that can be redirected.

Finally, here is some actual test code. The following methods are very similar. They make sure that when you try to insert all kinds of bookmarks the DB doesn't allow duplicate links. test_save_valid_bookmaek() just puts in a single bookmark and verifies that it has been added. test_duplicate_bookmark() tries to insert the same bookmark twice. test_duplicate_bookmark_name() tries to insert two bookmarks with the same name and test_duplicate_bookmark_url() tries to inserts two bookmarks with the same URL.

def test_save_valid_bookmark(self):     assert Bookmarks.select().count() == 0     params={'link':'http://another_valid.com',             'description':'good_one',             'bookmarkName':'valid_bookmark'}     self.save_new_bookmark(params)     assert Bookmarks.select().count() == 1     assert Bookmarks.get(1).link=='http://another_valid.com' def test_duplicate_bookmark(self):     assert Bookmarks.select().count() == 0     params={'link':'http://another_valid.com',             'description':'good_one',             'bookmarkName':'valid_bookmark'}     pairs = ['%s=%s' % (k, params[k]) for k in params]      url = '/save_bookmark/0?' + '&'.join(pairs)     # Will try to save the bookmark twice     self.save_new_bookmark(params, times=2)     assert Bookmarks.select().count() == 1     assert Bookmarks.get(1).link=='http://another_valid.com' def test_duplicate_bookmark_name(self):        assert Bookmarks.select().count() == 0    params={'link':'http://valid.com',            'description':'good_one',            'bookmarkName':'valid_bookmark'}    self.save_new_bookmark(params)    params['link'] = params['link'].replace('valid.com', 'different.com')    self.save_new_bookmark(params)    assert Bookmarks.select().count() == 1    assert Bookmarks.get(1).link=='http://valid.com' def test_duplicate_bookmark_url(self):    assert Bookmarks.select().count() == 0    params={'link':'http://valid.com',            'description':'good_one',            'bookmarkName':'valid_bookmark'}    self.save_new_bookmark(params)    params['name'] = params['name'].replace('valid_bookmark',                                            'different_name')    self.save_new_bookmark(params)    assert Bookmarks.select().count() == 2    assert Bookmarks.get(1).link=='http://valid.com'


If you run nosetests, you will get the following output:

====================================================================== FAIL: test_duplicate_bookmark_url (test_gigi.BookmarkTest) ---------------------------------------------------------------------- Traceback (most recent call last):   File "/Users/gsayfan/Documents/docs/Publications/TG Book/svn/trunk/code/5/book- marker/bookmarker/tests2/test_gigi.py", line 91, in test_duplicate_bookmark_url     assert Bookmarks.select().count() == 1 AssertionError ---------------------------------------------------------------------- Ran 4 tests in 1.558s FAILED (failures=1)


The test_duplicate_bookmark_url() failed. It is possible to enter two bookmarks with different names that have the same URL. Perhaps this is a bug, and perhaps it isn't. Feel free to decide either way and modify the bookmarker application or the test to match your decision.




Rapid Web Applications with TurboGears(c) Using Python to Create Ajax-Powered Sites
Rapid Web Applications with TurboGears: Using Python to Create Ajax-Powered Sites
ISBN: 0132433885
EAN: 2147483647
Year: 2006
Pages: 202

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net