Kung Fu Kung Fu

Fujian White Crane Kung Fu

Zope Zope

What I have and am doing with Zope

Photos Photos

Photoalbum, both old and new.

Receptsamlingen Receptsamlingen

In Swedish only. About my "Collection of Recipes" website.

Contact me Contact me

My contact details and how to contact me.

  Mobile version of this page Mobile version of this page


 

Mocking a Python standard library

pycon, gheorghiu, unit tests, automated testing, mocking, monkey, monkey-patching, unit/integration, pop3

14th of March 2008

Here's one of many things I've learnt today at PyCon. Inspired by code that Grig Gheorghiu showed in his slides on automated testing, you can monkey patch a standard library that your application is using in your unit tests to, in my case, mock a remote service without having to run a server. I've done lots of monkey-patching in Zope but then I've only been monkey patching individual methods or attributes of imported classes. This is very similar to that. Here's what my application does:

 from poplib import POP3
 class MyZopeApp(...):
    def check4mail(self, hostname, port, user, pwd):
        connection = POP3(hostname, port=port)
        ...download emails and process them...

Adjacent to this I have a unit/integration test that looks like this:

 class TestCase(ZopeTestCase):
    def test_check4mail(self):
        # monkey patch!
        # note that this imports a module, not a class
        from Products.IssueTrackerProduct import IssueTracker 
        FakePOP3.files = ('test1.email',)
        IssueTracker.POP3 = FakePOP3

        # now check what happens when check4mail() is run
        result = self.folder.tracker.check4mail()
        assert ...

Now for the mock. The mock is a fake POP3 class that instead of getting its data from the network reads local filesystem files. Here's what the code for FakePOP3 is:

 from poplib import POP3, error_proto

 class FakePOP3(POP3):

    username = 'test'
    password = 'test'
    files = []

    def __init__(self, hostname, port=110):
        self.hostname = hostname
        self.port = port

    def getwelcome(self):
        return "Welcome to fake account"

    def user(self, user):
        if user != self.username:
            raise error_proto("Wrong username.")

    def pass_(self, pswd):
        if pswd != self.password:
            raise error_proto("Wrong password.")

    def list(self, which=None):
        # eg. ('+OK 4 messages:', ['1 71017', '2 2201', '3 7723', '4 44152'], 34)
        files = self.files
        responses = []
        for i, f in enumerate(files):
            responses.append('%s %s' % (i+1, os.stat(f)[stat.ST_SIZE]))
        return ('+OK %s messages:' % len(files), responses, None)

    def retr(self, which):
        # ['response', ['line', ...], octets]
        filename = self.files[which-1]
        return ('response', open(filename, 'r').xreadlines(), None)

    def quit(self):
        pass

That's it! That's how you fake a POP3 server without having to run an actual mock server which could have been a solution.


Comment

Peter Bengtsson - 16th March 2008  [«« Reply to this]
A less advanced alternative is minimock by Ian Bicking:
http://blog.ianbicking.org/minimock.html
 
Name:
Email:
hide my email address.

Your email address will be encoded to prevent email-extraction spiders from reading it so you won't get spammed if you decide to show your email address.