I've been working on understanding how to do testing of TG apps.
The TG book mentions mechanize.  I didn't like how it talks to the
TG server through the web interface.  My other unittests use the
testutil module to issue requests directly though createRequest,
and I liked that, instead of starting the server.  For example, I
get to recreate my test data set every time.

I wrote an adapter to replace Mechanize's HTTPHandler.  If
the URL request is made to a given host/port it is intercepted
by the adapter, converted into a TG request, processed by
the web app, and the TG response converted into the form
that Mechanize expects.

Here it is

import httplib
import mechanize
from mechanize import _http, _response
from turbogears import testutil
import cherrypy
import rfc822

try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO

class TGHTTPHandler(_http.HTTPHandler):
    """intercept requests to the given http host:port and call
TurboGears directly"""
    def __init__(self,
                 intercept_host = "localhost",
                 intercept_port = "8080",
                 intercept_address = "127.0.0.1",
                 debuglevel=0):
        _http.HTTPHandler.__init__(self, debuglevel)
        self.intercept_host = intercept_host
        self.intercept_port = intercept_port
        self.intercept_address = intercept_address

    def http_open(self, req):
        # The request is either the hostname or the hostname:port
combination
        # Normalize to make it easier to compare.
        host = req.get_host()
        if ":" in host:
            host, port = host.split(":", 1)
        else:
            port = "80"

        # if self.intercept_port is None then should I intercept all
ports
        # to the intercept machine?
        if (host != self.intercept_host or port !=
self.intercept_port):
            # Let other requests go through.  Useful when testing that
the
            # TG app correctly links to external URLs
            return _http.HTTPHandler.http_open(self, req)

        # Pretty much copied this from what mechanize does to make the
request
        headers = dict(req.headers)
        headers.update(req.unredirected_hdrs)
        headers["Connection"] = "close"  # do not support HTTP 1.1
pipelining
        # normalize request header keys
        headers = dict( [(k.title(), v) for (k,v) in
headers.items()] )

        # convert a POST document into a file-like object
        rfile = None
        if req.data:
            rfile = StringIO(req.data)

        # Call the local TG server.  Here is the createRequest
signature
        #   create_request(request, method='GET', protocol='HTTP/1.1',
headers={}, rfile=None,
        #     clientAddress='127.0.0.1', remoteHost='localhost',
scheme='http')
        # XXX TG has no way to override the port?
        testutil.createRequest(request = req.get_selector(),
                               method = req.get_method(),
                               headers = headers,
                               rfile = rfile,
                               clientAddress = self.intercept_address,
                               remoteHost = host)

        # Convert the TG response to the expected form for Mechanize
        response = cherrypy.response
        fp = StringIO(response.body[0])
        code, msg = response.status.split(None, 1)
        code = int(code)
        # Mechanize uses an rfc822.Message and not a dictionary of
items
        headers = rfc822.Message(StringIO(""))
        for (k,v) in response.headers.sorted_list():
            headers[k] = v

        # This is a bit of a waste because downstream processors end
        # up making this seekable, even though it already is seekable
        return _response.closeable_response(
            fp, headers, req.get_full_url(), code, msg)


def Browser(*args, **kwargs):
    b = mechanize.Browser(*args, **kwargs)
    # remove the old HTTPHandler -- this is a hack and I don't know
the
    # right way to remove an old handler.
    b.handlers = [h for h in b.handlers if not isinstance(h,
_http.HTTPHandler)]
    # add the intercepting handler
    b.add_handler(TGHTTPHandler())
    return b

###### Example use -- I have a project called "pachy3"

from pachy3.controllers import Root
from turbogears import startup

def go():
    cherrypy.root = Root()
    startup.startTurboGears()
    b = Browser()
    b.set_handle_robots(False)

    b.open("http://localhost:8080/";)
    print b.title()

    startup.stopTurboGears()

go()
        Andrew Dalke
        [EMAIL PROTECTED]


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"TurboGears" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/turbogears?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to