Section 4.2. Testing TurboGears Applications


4.2. Testing TurboGears Applications

So now we have a working page, and it was easy enough to make. But before we go on adding more and more functionality to our page, let's take a quick break and look at how you write tests for TurboGears applications. It's never too early to start writing tests. In fact, TurboGears is designed to suport Test Driven Development, in which you write your tests first, and code second.

We don't have time to go over all the valuable information that has been written in support of writing unit and functional tests. If you're not convinced already, we're not going to force you to change the way you write code. For those who are convinced that writing tests early and often will save time on every change you make later, however, we're going to cover testing right up front.

TurboGears provides simple integration with Nose (http://somethingaboutorange.com/mrl/projects/nose/). With a simple nosetests command, you can run unit test.TestCase objects, doctests, or simple test functions in a module.

To run your tests, just type nosetests in the top-level directory of your project. Nose will then look through your project's directories to find whatever tests it can and run them all. Nose isn't magic; it just looks for files with test in the name, and methods/functions with test in the name, and runs those.

Before we start writing tests, we should take a look at the turbogears.testutil helper functions that we can use to make writing tests easier. The first function is testutil.call, which calls one of your controller methods and returns the dictionary directly without applying the Kid template to it. That makes it really easy to double check that your method does the right thing without worrying about how the result is presented to the user.

TurboGears quickstart automatically creates a couple of sample tests that will pass if you run nosetests on a freshly quickstarted project. You can see those tests in the test_controllers.py file in your /tests directory.

When you understand them, you can erase them and add a new test that does a simple check on the controller we just created, by using the testutil.call function:

from turbogears import testutil from bookmark.controllers import Root import cherrypy def test_list_controller():    """list method should return a set of bookmark objects called bookmarks."""    cherrypy.root = Root()    output = testutil.call(cherrypy.root.list)    assert output.has_key('bookmarks')


This test is simple; but if you try to expand it to test the value associated with the bookmarks key, your tests will start failing. This is because we haven't set up any database configuration for our tests.

Generally, you don't want to run your tests against your actual database; you want a test database. Luckily, testutils has a class DBTest, which makes writing tests that interact with the database remarkably easy.

Here's a sample of a simple test that connects to a separate test database:

from turbogears import database, testutil from bookmarker.model import Bookmark import cherrypy database.set_db_uri("sqlite:///:memory:") class test_full_stack(testutil.DBTest):     def get_model(self):     return Bookmark def test_list_template(self):     """Checks to see that our template is applied"""     testutil.createRequest("/list")     assert '<TITLE>Boomarker</TITLE>' in cherrypy.response.body[0]


We have a few extra imports in this test module. From TurboGears, we need testutil again, as well as database. And of course, if we're going to use our Bookmark class, we'll have to import that from our bookmarker.model module, too.

The first thing this module does is set up the database connection URI. The set_db_uri() method is pretty self-explanatory, but it's worth noticing that we can : memory to indicate that our SQLite database ought to exist only in memory. This is great for tests because it's fast and transitory.

The actual test_list_template test is pretty simple. But it uses another testutil function, createRequest(). This function calls through the whole stack for whatever URL you pass it. In this case, it's calling the list method, and rendering the results through the template. The final results are stored in the cherrypy.response.body list, so we can easily check that the expected results are present there.

Looking at this test, it's not immediately clear exactly how it uses the database. This test is using basic string functionality to make sure that particular text is present in the response. This is more of a functional test than a unit test because it examines the final, rendered output. Because this renders a template that iterates through all the bookmarks in the database, it won't work unless it can find a database connection.

This test adds a record to the database, and then checks the response to ensure that the proper link shows up in the final template.

Given all of this, you can easily write tests that do more interesting functional tests. For example, it's probably worthwhile to write a quick test that adds a record to the database and then checks the /list page to see that the proper link shows up in the final output. That way, if anything goes wrong in the middle, we should get a failing test. Here's a simple functional test that does exactly that:

    def test_list_contents(self):         """If we add a record to the model, it should            show up in the final page text"""         Bookmark(name='Compound Thinking',                   link='http://www.CompoundThinking.com',                   description="A {not so} random link.")         testutil.createRequest("/list")     assert '<A HREF="http://www.CompoundThinking.com">' in cherrypy.response. body[0]


Note

If you use Python's doctest functionality to embed texts in docstrings, Nose will find and run these tests, too. But you should be aware that doctests don't provide output in a way that Nose can record; so you'll only get pass/fail information on these tests. This isn't a big hurdle, because you can always run the doctests on that particular module manually and get more detailed information on why that particular test failed.





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