Index: turbogears/identity/tests/test_identity.py
===================================================================
--- turbogears/identity/tests/test_identity.py	(revision 5265)
+++ turbogears/identity/tests/test_identity.py	(working copy)
@@ -155,7 +155,7 @@
 
 
 
-class TestIdentity(testutil.TGWebTest):
+class TestIdentity(testutil.TGTest):
 
     def setUp(self):
         # identity requires visit and a failure_url
@@ -169,12 +169,12 @@
         self._original_config = original_config
         config.configure_loggers(test_config)
         config.update(test_config['global'])
-        testutil.mount(IdentityRoot())
-        testutil.TGWebTest.setUp(self)
+        self.root = IdentityRoot
+        testutil.TGTest.setUp(self)
         self.init_model()
 
     def tearDown(self):
-        testutil.TGWebTest.tearDown(self)
+        testutil.TGTest.tearDown(self)
         config.update(self._original_config)
 
     def init_model(self):
Index: turbogears/tests/test_controllers.py
===================================================================
--- turbogears/tests/test_controllers.py	(revision 5265)
+++ turbogears/tests/test_controllers.py	(working copy)
@@ -242,11 +242,12 @@
         return "redirected OK"
 
 
-class TestRoot(testutil.TGWebTest):
+class TestRoot(testutil.TGTest):
 
     def setUp(self):
         testutil.mount(MyRoot(), '/')
         testutil.mount(SubApp(), '/subthing')
+        self.app = testutil.make_app()
         super(TestRoot, self).setUp()
 
 
@@ -565,12 +566,13 @@
         assert 'handling_key' in response
 
 
-class TestURLs(testutil.TGWebTest):
+class TestURLs(testutil.TGTest):
 
     def setUp(self):
         testutil.mount(MyRoot(), '/')
         testutil.mount(SubApp(), '/subthing')
         testutil.mount(SubApp(), '/subthing/subsubthing')
+        self.app = testutil.make_app()
         super(TestURLs, self).setUp()
 
     def tearDown(self):
Index: turbogears/tests/test_view.py
===================================================================
--- turbogears/tests/test_view.py	(revision 5265)
+++ turbogears/tests/test_view.py	(working copy)
@@ -3,8 +3,12 @@
 import cherrypy
 import unittest
 
-class TestView(testutil.TGWebTest):
+class TestView(unittest.TestCase):
 
+    def setUp(self):
+        # The server needs to be started so that the template engines get loaded.
+        testutil.start_server()
+
     def test_cycle(self):
         oe = view.base.cycle(('odd','even'))
         assert str(oe) == str(None)
Index: turbogears/tests/test_paginate.py
===================================================================
--- turbogears/tests/test_paginate.py	(revision 5265)
+++ turbogears/tests/test_paginate.py	(working copy)
@@ -186,7 +186,22 @@
     return result
 
 
-class TestSpy(testutil.TGWebTest):
+class TestPagination(testutil.TGTest):
+    """Base class for all Paginate TestCases"""
+
+    def setUp(self):
+        self.root = self.MyRoot
+        super(TestPagination, self).setUp()
+
+    def request(self, url, status=200):
+        response = self.app.get(url, status=status)
+        self.body = response.body
+        if "fail: " in self.body:
+            print self.body
+            assert False, "Spy alert! Check body output for details..."
+
+
+class TestSpy(TestPagination):
     """Never trust a spy"""
 
     class MyRoot(RootController):
@@ -219,11 +234,6 @@
             spy = Spy(foobar=10)
             return dict(data=data, spy=spy)
 
-
-    def setUp(self):
-        super(TestSpy, self).setUp(self.MyRoot)
-
-
     def test_spy(self):
         response = self.app.get('/spy')
         Spy.assert_ok(response.body, 'current_page', 1)
@@ -251,20 +261,6 @@
         Spy.assert_ok(response.body, 'var_name', "'data'", raw=True)
 
 
-class TestPagination(testutil.TGWebTest):
-    """Base class for all Paginate TestCases"""
-
-    def setUp(self):
-        super(TestPagination, self).setUp(self.MyRoot)
-
-    def request(self, url, status=200):
-        response = self.app.get(url, status=status)
-        self.body = response.body
-        if "fail: " in self.body:
-            print self.body
-            assert False, "Spy alert! Check body output for details..."
-
-
 class TestBasicPagination(TestPagination):
 
     class MyRoot(RootController):
Index: turbogears/qstemplates/quickstart/+package+/tests/test_model.py_tmpl
===================================================================
--- turbogears/qstemplates/quickstart/+package+/tests/test_model.py_tmpl	(revision 5265)
+++ turbogears/qstemplates/quickstart/+package+/tests/test_model.py_tmpl	(working copy)
@@ -17,13 +17,12 @@
     from ${package}.model import YourModelClass, User, ...
 """
 
-from turbogears import testutil, database
+from turbogears.testutil import DBTest
 
 # from ${package}.model import YourModelClass, User
 
-# class TestUser(testutil.DBTest):
-#     def get_model(self):
-#         return User
+# class TestUser(DBTest):
+#     model = User
 #
 #     def test_creation(self):
 #         """Object creation should set the name."""
Index: turbogears/qstemplates/quickstart/+package+/tests/test_controllers.py_tmpl
===================================================================
--- turbogears/qstemplates/quickstart/+package+/tests/test_controllers.py_tmpl	(revision 5265)
+++ turbogears/qstemplates/quickstart/+package+/tests/test_controllers.py_tmpl	(working copy)
@@ -4,34 +4,24 @@
 from ${package}.controllers import Root
 import cherrypy
 
-testutil.mount(root = Root())
+class TestPages(testutil.TGTest):
 
-class TestPages(unittest.TestCase):
+    root = Root
 
-    def setUp(self):
-        testutil.start_server()
-
-    def tearDown(self):
-        """Tests for apps using identity need to stop CP/TG after each test to
-        stop the VisitManager thread.
-        See http://trac.turbogears.org/turbogears/ticket/1217 for details.
-        """
-        testutil.stop_server()
-
     def test_method(self):
         "the index method should return a string called now"
         import types
-        response = testutil.go("/")
+        response = self.app.get("/")
         assert type(response.raw["now"]) == types.StringType
 
     def test_indextitle(self):
         "The indexpage should have the right title"
-        response = testutil.go("/")
+        response = self.app.get("/")
         assert "<title>welcome to turbogears</title>" in response.body.lower()
 
 #if $identity != "none"
     def test_logintitle(self):
         "login page should have the right title"
-        response = testutil.go("/login")
-        assert "<title>login</title>" in response.body.lower()
+        response = self.app.get("/login")
+        assert "<title>Login</title>" in response, response
 #end if
Index: turbogears/qstemplates/quickstart/setup.py_tmpl
===================================================================
--- turbogears/qstemplates/quickstart/setup.py_tmpl	(revision 5265)
+++ turbogears/qstemplates/quickstart/setup.py_tmpl	(working copy)
@@ -27,6 +27,7 @@
 
     install_requires=[
         "TurboGears >= ${turbogearsversion}",
+        "WebTest",
 #if $sqlobject == 'True'
         "$sqlobjectversion"
 #elif $sqlalchemy == 'True'
Index: turbogears/testutil.py
===================================================================
--- turbogears/testutil.py	(revision 5265)
+++ turbogears/testutil.py	(working copy)
@@ -157,29 +157,34 @@
     return TestApp(wsgiapp)
 
 
-class TGWebTest(unittest.TestCase):
+class TGTest(unittest.TestCase):
     """A WebTest enabled unit testing class.
 
-    This allows testers to subclass us and use self.app to make WebTest calls.
+    To use, subclass & set root to your controller object, or set app to a 
+    webtest.TestApp instance.  
 
+    In your tests, use self.app to make WebTest calls.
     """
 
-    def setUp(self, controller=None):
-        """Set up the WebTest by starting the server."
+    root = None
+    app = None
 
+    def setUp(self):
+        """Set up the WebTest by starting the server.
+
         You should override this and make sure you have properly
         mounted a root for your server before calling super,
         or simply pass a root controller to super.
         Otherwise the Cherrypy filters for TurboGears will not be used.
-
         """
-        self.app = make_app(controller)
+        assert self.root or self.app, "Either self.root or self.app must be set"
+        if not self.app: 
+            self.app = make_app(self.root) 
         start_server()
 
     def tearDown(self):
         """Tear down the WebTest by stopping the server."""
         stop_server(tg_only = True)
-        del self.app
 
     def login_user(self, user):
         """Log a specified user object into the system."""
@@ -281,10 +286,21 @@
     return output, response
 
 
-class DBTest(unittest.TestCase):
+class AbstractDBTest(unittest.TestCase):
+    """A database enabled unit testing class.
 
+    Creates and destroys your database before and after each unit test.  You must set the 
+    model attribute in order for this class to function correctly.
+    """
     model = None
 
+    def setUp(self):
+        raise NotImplementedError()
+
+    def tearDown(self):
+        raise NotImplementedError()
+
+class DBTestSO(AbstractDBTest):
     def _get_soClasses(self):
         try:
             return [self.model.__dict__[x] for x in self.model.soClasses]
@@ -295,8 +311,7 @@
         if not self.model:
             self.model = get_model()
             if not self.model:
-                raise "Unable to run database tests without a model"
-
+                raise Exception("Unable to run database tests without a model")
         for item in self._get_soClasses():
             if isinstance(item, types.TypeType) and issubclass(item,
                 sqlobject.SQLObject) and item != sqlobject.SQLObject \
@@ -311,6 +326,25 @@
                 and item != InheritableSQLObject:
                 item.dropTable(ifExists=True, cascade=True)
 
+class DBTestSA(AbstractDBTest):
+    def setUp(self):
+        database.get_engine()
+        database.metadata.create_all()
+
+    def tearDown(self):
+        database.metadata.drop_all()
+
+
+#Determine which class to use for "DBTest".  Setup & teardown should behave 
+#simularly regardless of which ORM you choose.
+if config.get("sqlobject.dburi"):
+    DBTest = DBTestSO
+elif config.get("sqlalchemy.dburi"):
+    DBTest = DBTestSA
+else:
+    raise Exception("Unable to find sqlalchemy or sqlobject dburi")
+
+
 def unmount():
     """Remove an application from the object traversal tree."""
     # There's no clean way to remove a subtree under CP2, so the only use case
Index: turbogears/command/quickstart.py
===================================================================
--- turbogears/command/quickstart.py	(revision 5265)
+++ turbogears/command/quickstart.py	(working copy)
@@ -240,7 +240,7 @@
             sqlalchemyversion = str(get_requirement('sqlalchemy'))
             cmd_args.append("sqlalchemyversion=%s" % sqlalchemyversion)
         if self.elixir:
-            elixirversion = str(get_requirement('future', 'elixir'))
+            elixirversion = str(get_requirement('sqlalchemy'))
             cmd_args.append("elixirversion=%s" % elixirversion)
 
         command.run(cmd_args)

