Mocking wxpython for unit testing

Unit testing GUI code is usually a pain, but Python makes this so easy that I laugh with glee every time I do it.

My approach is to declare my own fake versions of the main wxpython widgets, and then overwrite the wx namespace with my fake classes. This idea is basically borrowed from a post on dirt simple. What would be serious black-magic voodoo in a lot of languages is a simple two-liner in python:

# Fake classes declared above
import wx
wx.__dict__.update(locals())

OK, so maybe you're saying, hey, you're not actually testing your wxpython code! Here's the deal:

  1. I don't really want to unit test wxpython.
  2. I want to mock elements that require user input, like file/dir dialogs, etc. This approach lets me simulate user interaction with the fake classes.
  3. Creating a wx.App for every test is going to significantly slow down the execution of our unit tests. If my unit tests take 5 seconds to run instead of 2, I'm simply going to run them less often.

I'll test the actual GUI functionality using function/acceptance tests.

So now in my unit tests, all I need to do is import fakewidgets before my GUI code module(s), and fakewidgets works its magic.

>>> import wx
>>> frame = wx.Frame(None)
Traceback (most recent call last):
   <…traceback ommitted…>
wx._core.PyNoAppError: The wx.App object must be created first!
>>> import fakewidgets # Will overwrite wx namespace
>>> frame = wx.Frame(None)
>>> frame
<fakewidgets.Frame instance at 0x00C7F710>
>>>

The linked zip file (fakewidgets.zip) has my fakewidgets module. It's still not complete, since I've simply been adding classes/methods as I needed them for my unit tests, but it should be straightforward enough to add in what else you need. If you do, please pass your additions back on!

Here's how you might use the module to unit test your GUI code. Below is a very simple/silly program that pops up a frame window.

"""
simpleframe.py
"
""

import wx

class SimpleFrame(wx.Frame):

    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

class SimpleApp(wx.App):

    def OnInit(self):
        frame = SimpleFrame(parent=None, title="Simple Frame")
        frame.Show()
        return 1

def main():
    app = SimpleApp(0)
    app.MainLoop()

if __name__ == "__main__":
    main()

Here's how you could unit test this (I'm assuming that you're using the excellent nose module for unit testing).

import fakewidgets
import simpleframe

def test_main():
    simpleframe.main()

class TestSimpleFrame(object):

    def setup(self):
        self.frame = simpleframe.SimpleFrame(None, title="Simple")

    def test_create(self):
        pass

class TestSimpleApp(object):

    def setup(self):
        self.app = simpleframe.SimpleApp(0)

    def test_create(self):
        pass

    def test_on_init(self):
        retval = self.app.OnInit()
        assert retval == 1, retval

Look ma, no wx.App! We can even call functions that call App.MainLoop() with impunity.

8 comments to Mocking wxpython for unit testing

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>