Testing XMLRPC Controllers in Pylons

Testing is one of the essential tasks in modern software development, so
it is only natural to want to test an application as thoroughly as
possible.

I’m currently working on an application that offers a service via
xmlrpc. For a client side implementaion I use python’s xmlrpclib and
that is working well. Then again, I obviously want to test the xmlrpc
functionality also during regular unit and functional tests and not only
using the client. What’s more, having to instantiate a server to be able
to run tests is cumbersome at best.

Unfortunatly, Pylons does not offer a method to test xmlrpc controllers
out of the box. The solution I found is not quite as complicated as I
had feared at first. Read more after the break.

For the above mentiond reasons, here is my solution for testing a
XMLRPCController in Pylons using a mock transport for xmlrpclib.

This solution is largely based on the excellent post by Jean Schruger
on his Blog
.

from myapp.tests import *

import sys
from StringIO import StringIO
import xmlrpclib
from xmlrpclib import ServerProxy

class WSGILikeHTTP(object):
  def __init__(self, host, app):
    self.app = app
    self.headers = {}
    self.content = StringIO()

  def putrequest(self, method, handler):
    self.method = method
    self.handler = handler

  def putheader(self, key, value):
    self.headers[key] = value

  def endheaders(self):
    pass

  def send(self, body):
    self.body = body

  def getfile(self):
    return self.content

  def getreply(self):
    if self.method == "POST":
      r = self.app.post(self.handler,
                        headers = self.headers,
                        params = self.body )
      self.content = StringIO(r.response.unicode_body)
    return (200, None, None)

class WSGIAppTransport(xmlrpclib.Transport):
  # Only here to pass the 'app'
  def __init__(self, app):
    xmlrpclib.Transport.__init__(self)
    self.app = app

  # return the fake httplib.HTTP(host)
  def make_connection(self, host):
    host, extra_headers, x509 = self.get_host_info(host)
    return WSGILikeHTTP(host, self.app)


class TestXmlrpcController(TestController):

    def test_index(self):
      server = ServerProxy('http://admin:admin@dummy/xmlrpc',
                            transport=WSGIAppTransport(self.app))
      print >> sys.stderr, server.system.listMethods()

Generating Faults in Pylons XMLRPCController

This might seem easy enough, but it took me a while to get it right
since the Pylons documentation is a bit misleading here, really. It says
you should use xmlrpc_fault from pylons.controllers.xmlrpc but
that’s actually not working if you’re doing something like:

from pylons.controllers.xmlrpc import xmlrpc_fault
...
return xmlrpc_fault( 101, "My Error" )

This will wrap an xmlrpclib.Fault into a
pylons.controllers.util.Reponse object which will fail to marshal
with something like:

TypeError: cannot marshal  objects

The correct way to do it is:

import xmlrpclib...return xmlrpclib.Fault( 101, "My Error" )

Django on Windows

To more easily use Django on Windows, create a dos .bat file in
Django’s bin directory named django-admin.bat with the following
content:

@%~dp0\..\..\..\..\python.exe %~dp0\django-admin.py %*

and add Django’s bin directory to your PATH:

set DJANGO_HOME=C:\Development\tools\Python25\Lib\site-packages\django
set PATH=%DJANGO_HOME%\bin;%PATH%

You can now use django-admin from anywhere without specifying the
full path.

(I kept getting ImportError: No module named django.core when trying
to invoke django-admin.py from the commandline)