Python Programming, news on the Voidspace Python Projects and all things techie.
Dependency Injection and Mock(ing) for Testing
This blog entry is about changes to Mock - A Lightweight Mocking Framework for Testing, one of the projects (and thankfully small) that I will be working on once 'the book' is finally out of the way.
Suppose you have the following code, how do you test some_method:
def some_method(self, arg):
self.other = OtherClass(arg)
Well, we could just call some_method, but what if OtherClass is expensive to construct (or establishes a database connection) and we'd really rather not do that in a unittest?
We can't straightforwardly mock, because the construction of the class is internal to the method. One technique is to use dependency injection, where the dependency class is passed in. We can even make it the default value of a keyword argument:
def some_method(self, arg, Dependency=OtherClass):
self.other = Dependency(arg)
Now it is trivial to test some_method. We can pass in a mock object as the 'Dependency' and test that the body of the method acts on it as it should. The problem I have with this approach is that we have now added code that sets up the default dependency (the default value of the keyword argument), effectively another layer, that we should test as well. (If we only with an explicitly passed in dependency then we test under different conditions to how the code is called in production.)
This approach has other advantages however. The dependency on OtherClass is no longer 'hardwired' and we could use another class or callable at runtime if we wanted to change the behaviour. Of course this could be catering to a future and purely imagined usecase, and YAGNI applies. There is a lot more to dependency injection than this simple example, and it can be an extremely useful technique, if you need it.
At Resolver Systems we take an alternative approach to testing this kind of code, generally mocking instead of using dependency injection. When I discussed with one of my fellow developers whether we should use more dependency injection to reduce the amount of mocking needed in our tests, his response surprised me:
As far as I can tell, dependency injection just screws with your interface.
And it's true. In this example, the first version represented the code we wanted to write. We only modified it, changing the interface in the process, to make it easier to test. Not only does reading the code now give us a distorted view of its use, but tools that use introspection (like automated documentation tools) will also pick up on the changed API.
A standard Python way of testing the first version of the code is to mock out OtherClass at the module level. Because names are resolved dynamically, instead of being statically bound, if we patch the class for the duration of the test then our mock will be used when we call some_method. Unfortunately the code to do this requires a bit of ugly and tedious boilerplate:
original = SomeModule.OtherClass
SomeModule.OtherClass = Mock()
something = SomeClass()
# test our Mock is used in the correct way
SomeModule.OtherClass = original
Most mocking frameworks are based on the 'record - > verification' pattern for testing. Not only do I find this backwards (I prefer the 'action -> assertion' pattern) but it means you often have to record every step in a test, when you are only interested in testing a corner case - one part of the unit behaviour.
Mock is a very lightweight mocking framework for unit testing. It also includes a decorator that makes it trivial to mock out classes in a module without the boilerplate.
def test_some_method(self, MockOtherClass)
MockSomeClass.return_value = sentinel.Instance
something = SomeClass()
The patch decorator monkey patches SomeModule.SomeClass only within the scope of the test and passes in the mock it creates to the test method. Actually the example above uses a couple of nice features of Mock (and its patch decorator) that aren't quite available yet. The code doesn't look very different with the current version though.
Of course in Python, modules are first class objects and are effectively singletons. This is one reason why the singleton pattern is so rarely useful in Python. It also means that changing module level attributes for tests is mutating global state. Forgetting, or botching, restoring state after your test will leave your patch in place for subsequent tests and cause you some very weird problems. This is reason enough for some people to say that it should never be done. One real consequence is that you can't run tests in parallel - but whilst running tests in parallel is fine, would you really do it using several threads rather than several processes? That's a recipe for disaster all of its own... Using patch ensures that the patching is undone whether the test passes or fails.
I originally created the Mock module to make some common testing patterns we use at Resolver Systems simpler. Since then we've made several improvements to Mock that have never been synced back in (plus I've had patches and feature requests from other users of Mock):
- A patch (1 line!) from Kevin Dangoor for nose compatibility (preserving line numbers in patched methods)
- Add the assert_called_with method
- Add a side_effect attribute - a callable used every time a Mock is called. This allows you to do nice things like raise an exception if a mock is called, or return members of a sequence for repeated calls rather than just a fixed value.
- Make the default return value a Mock (unless None is explicitly specified)
- Modify patch to allow a single string argument specifying what should be patched
Another project I omitted from my list of projects I'm interested in was Tweezer. Tweezer is a very nice looking Twitter client by Ed Leafe, built on the Dabo framework. It's still missing some features, but I'm looking forward to hacking on it...
|||Over the last year I've also averaged more than five entries per week on the IronPython URLs blog, but as that is a link and news aggregator they tend to be a lot shorter.|
Try Python (Python in the Browser) and Silvershell
One of the longer term projects I'd like to work on is the Try Python tutorial. The website currently just shows Python in the Browser, which is an interactive Python interpreter running in an HTML textarea on the webpage. The nice thing is that it is possible to preload samples into the interpreter and use it as live examples for a Python tutorial. This would be a great way to teach Python, requiring only a browser with Silverlight 2 .
Creating an effective tutorial is actually quite hard work, so it would be nice to build off an existing project. Crunchy is a related project that includes some tutorials already, but the 'textarea' type approach looks closer to the real Python interpreter than the single text entry field that Crunchy currently uses. I wonder whether integration with Crunchy would be an effective way to go - I really want to spend my time on making the tutorial effective rather than working on the implementation details of the interpreter.
That aside, "Python in the Browser" is currently not as visually appealing as it could be... There are at least three different ways for me to remedy this.
- Rebuild on Silvershell.
As you can see it is very visually appealing. It also has some nice features like auto-indentation and completion (neither of which would be particularly difficult to add to PiB). The only difficulty is that it might make it harder to simulate several interpreters in a page as it certainly used to be difficult to relocate the Silverlight control in the page . Dan suggests we could solve that by locating the control in an iframe (my instinct was to spell that iFrame...) and relocating that.
Anyway it will be fun to experiment and I hope to end up with a useful set of resources for learning Python online.
|||And once Silverlight 2 is out of beta it will almost certainly be pushed out over Windows update like Silverlight 1. Not long ago Silverlight 1 downloads were running at a million a day  so I don't expect deployment to be an issue.|
|||Well, it used to hard crash the browser anyway...|
Too Many Projects, Too Little Time
One annoying thing about spending the last year writing, is that I haven't been able spend much time on other projects (although I did recently manage to push out a ConfigObj release).
I've already spent (in my mind) the spare time I will have once the book is done three times over. Projects I have waiting in the wings include:
This is a Python documentation tool that Resolver Systems uses to auto-generate our Spreadsheet API Documentation from ReST docstrings. I've had permission to release this as Open Source, at least partly because I wrote half of it on my own time one Christmas. It's features (which are the reason we wrote a new documentation tool) are:
- Runs under IronPython and will generate docs for code written in IronPython (using introspection)
- Allows you to specify which parts of your API should be public
- Can raise an error if any public class / method / property does not have a docstring
- It is very small (only a few hundred lines of code) and easy to customize
- Fully tested of course
This is (or will be) a compatibility layer for the .NET base class libraries so that IronPython code can be run under CPython. This will be useful for me if we make a source release of the Resolver One Library packages. This will allow you to integrate Resolver One spreadsheets, or even the whole calculation engine, into standard Python applications.
As we haven't yet made a source release, it isn't the highest priority.
This project was started by Ivan Porto Carrero, the author of IronRuby in Action. It allows you to create user controls in XAML, for Windows Presentation Foundation and Silverlight, that are written in DLR languages (using normal XAML syntax for setting properties).
Ivan has got it working with IronRuby and I've promised to get it working with IronPython.
An IDE for Dynamic Language Runtime languages (IronPython & IronRuby), compatible with both Mono and .NET. Ben Hall has made a great start on this project. I'm not sure how much time I will have to contribute to this, but I'm watching it with great interest.
In fact there are several more (well three...) that deserve blog entries of their own.
One final one that I might take on is a report generation tool for Resolver One. My Dad works in Industrial Safety and Reliability Engineering and often has to produce hazard reports. The standard tools are custom applications (inflexible and unfamiliar to users) or Excel (not really suited to the purpose but much more flexible than the custom applications). If I can create a report generation library for Resolver One that produces Word documents, then not only will it be useful for me but I can build a tool for him that is as flexible as Excel but customized for the hazard reports he needs to produce.
Last Chapter of IronPython in Action Finished
The last chapter of IronPython in Action is finally done. There is still work to do before the book is complete (appendices, index, updating the first chapter in light of the last year in IronPython), but chapters 12 (Databases and Web Services written by Christian Muirhead) through to 15 are now ready to go to the final review phase. This will take a couple of weeks and after making any necessary changes it will then go through for publishing - which could take anything up to three months!
Chapter 15 has been one of the most fun chapters to write. It shows how to use IronPython as a scripting engine in .NET applications (from C# or VB.NET). The chapter table of contents is:
15 Embedding the IronPython Engine
15.1 Creating a custom Executable15.1.1 The IronPython Engine15.1.2 Executing a Python File
15.2 IronPython as a Scripting Engine15.2.1 Setting and Fetching Variables from a Scope15.2.2 Providing Modules and Assemblies for the Engine15.2.3 Python Code as an Embedded Resource
15.3 Python Plugins for .NET Applications15.3.1 A Plugin Class and Registry15.3.2 Auto-discovery of User Plugins15.3.3 Calling the User Plugins
15.4 Using DLR Objects from Other .NET Languages15.4.1 Expressions, Functions and Python Types15.4.2 Dynamic Operations with ObjectOperations15.4.3 The Builtin Python Functions and Modules
The chapter works through four examples:
- Creating a custom executable that launches a Python application
- Using IronPython as an embedded scripting engine - setting up the execution environment and setting and fetching variables in the execution scope
- Python for 'user plugins' in an application - including error handling, auto-discovery of plugins, exposing an API to the hosted Python engine and so on
- Interacting with dynamic objects from C# and VB.NET - creating instances of Python classes, calling functions and methods etc
PyCon UK: Talk and Tutorial Lists Up
This is our second PyCon UK conference, and this year we start with a full day of tutorials on the Friday (don't forget to include the tutorials when you book).
- Timetable for the tutorials day
- Abstracts of currently accepted talks
- Booking form (early bird rate is still open - but not for too much longer)
Christian Muirhead (my co-author for IronPython in Action), Menno Smits (another colleague) and I will be doing a four hour tutorial on developing with IronPython - so if there is anything you would like to see covered then get in touch!
We have some great talks and tutorials planned.
The tutorials (the list is shorter):
- Developing with IronPython
- An Introduction to Python (John Pinner)
- Building Solutions with Reportlab (Andy Robinson)
- Generator Tricks for Systems Programmers (David Beazley)
- Practical Python GUI Programming with PyQt4 (Mark Summerfield)
- An Introduction to Django (Jacob Kaplan-Moss)
Notable talks (way too many to list all of them):
- Stretching Pyglet's Wings (Jonathan Hartley - an erstwhile colleague)
- Amazon Web Services in the Cloud (Simone Brunozzi from Amazon.com)
- Practical concurrent systems made simple using Kamaelia (Michael Sparks)
- Talks on PyPy and py.test
- Core Python Containers - Under the Hood (Raymond Hettinger)
- Python 2.6 & 3.0 (Raymond Hettinger)
- Python & WMI (Tim Golden)
- Testing with Mock Objects (Simon Brunning)
- Functionally Testing GUI Applications (Me!)
- What's new in Django 1.0 (Jacob Kaplan-Moss)
- Using And Customising The Django Admin Interface (Simon Willison)
As you can see we have some national and international Python stars speaking, not to mention keynotes by Ted Leung (Sun) and Mark Shuttleworth (Canonical).
Neither the list of tutorials nor talks is final (so still time to submit). Menno has submitted a talk on Python programming with the OpenMoko phone and we're expecting to have a tutorial from 'a google guy' on developing for App Engine.
On the OpenMoko, it is nothing like as usable as the iPhone (more on that later) - but perhaps eventually. In the meantime, it is extremely interesting as an embedded Linux device with mobile phone, wifi and bluetooth capabilities.
Oh, we also have talks on SCons, Pylons, Google AppEngine, testing tools, Python in education at Leeds university, Python in science and so on. See you there.
|||Not including me unfortunately...|
Feature Freeze for Resolver One 1.2 Release
The first major update to Resolver One after our public release in January, was version 1.1 in June. The focus of the 1.1 release was performance improvements. Overall we achieved a 30% performance improvement, but for some specific bottlenecks we made massive improvements . As a result of this focus the list of new features in 1.1 wasn't very impressive.
The same is definitely not true of the list of improvements in the forthcoming 1.2 release. As you might expect, the list is a combination of implementing standard spreadsheet features, bugfixes and new features that build on what is unique about Resolver One.
The list doesn't include the major new feature of 1.2. It's a bit special (if you're into spreadsheets), and in my opinion is game-changing for developing spreadsheet applications. We're keeping it under wraps until the release.
Other than this there are still some impressive new features:
- Create dropdown lists in cells from cellranges (or any iterables) in user code
- Paste special: paste formulae/values/formatting/comments
- Drag down (or up or left or...) from bottom right of cell to fill a larger range with auto-incrementing values (effectively fill down for numbers and dates etc)
- Percentage formatting for displaying value - numbers are multiplied by 100 and get a "%" at the end.
- Percentages become legal syntax in formulae or as constants (direct values) in cells.
- Persistent worksheets stored across recalcs
- Support referencing cells in other Resolver One spreadsheets
Worksheet header lookups now by value, allowing integer (etc) headers
Support slicing on .Rows and .Cols iterators for Worksheets and CellRanges
For example: worksheet.Rows[3:12] will return rows 3 through 11 inclusive. You can also slice with headers rather than indices.
Wait cursor when running click handlers for users' buttons
Ability to make visible changes to the grid during a button handler
Custom Numeric Types right aligned in grid and used by SUM (recognised because they define __abs__)
Performance boost - due to an upgrade to PLY 2.5
A whole bunch of standard spreadsheet functions
There are a couple of other features already underway that will hopefully make it in as well.
You can see almost the full list of new features, including bugfixes and improvements in the Financial Edition of Resolver One, in the entry on Resolver Systems News Blog:
The new version should be available within the next two weeks (hopefully sooner but you know how it goes). As always the desktop version of Resolver One is free for non-commerical and Open Source use.
Once the release is out of the way I'm hoping to find time to write up some new articles on Resolver Hacks. One article I'd like to write is an example CRUD application using Resolver One, showing some 'best practices'  for developing applications with Resolver One. Particularly the use of the persistent worksheets that cache between recalcs are ideal for this.
While we finish off version 1.2 we're already planning 1.3, and William is making massive progress with Ironclad...
|||Indexing worksheets with headers went from being n-squared to O(1) for example.|
|||I know, I know. Better practises?|
This work is licensed under a Creative Commons Attribution-Share Alike 2.0 License.