Saturday, October 18, 2008

Newbie Nugget: Unit Testing with Mock

I presented my second “Newbie Nugget” at the October BayPIGgies meeting. Since I’ve been working with Mock quite a bit lately, I chose to present on that. (I also thought I could present on decorators, but I decided Mock would be easier since I’d have to do the least to prepare. I remember feeling confident on my knowledge of Mock itself, but I suspected there was still a lot I had to learn on unit testing. My suspicions proved correct.)

I didn’t make as much time to prepare as I would have liked. I ended up with 18 slides, and didn’t have time to trim them down. So I had to breeze through them very quickly. (In theory I only had 5 minutes. I don’t know how long I actually took—probably more like 10—it turned out there wasn’t any pressure this time to keep it brief.) I also would have liked to have time to come up with some real-world examples, but in my rush to finish the slides I ended up taking the code examples from Michael Foord’s documentation, except for the last two examples which I pulled out of my own code. I’ve uploaded the Keynote slides exported to PDF and to HTML. To save some of you some time, the references I had in my final slide are:

Note that I prepared this presentation based upon Mock version 0.3.1. Michael has since released version 0.4.0. TODO: I’d like to describe the details on what’s new in 0.4.0—but in the meantime, see the notes in the documentation or Michael’s blog post.

After the main presentation on PyGameSF, there was some extra time left and Jim Stockford asked if I’d be willing to take questions. I’m glad he asked, and I’m glad I did so. (How could I not?) I probably will not recall all the questions. Feel free to ask them again here, or on the BayPIGgies mailing list.

The first question I remember was whether I’ve found any bugs in Mock. I replied no, and elaborated that Mock was written using test-driven development and the tests themselves use Mock (and are included with Mock). So if you do find a bug, you can easily fix it yourself. (But looking at the changelog, it appears there have been few bugs fixes. Most changes have been new features. BTW, I’ve submitted a couple patches with new features myself, but I believe Michael decided he wasn’t ready for them or decided to implement them in a different way. TODO: I have a couple ideas for other new features to add.) And Mock’s unit tests are good examples of how to use Mock.

Then Alex Martelli commented (I probably won’t get this right) that my use of the @patch decorator in my tests is dangerous and won’t scale. (It’s not clear to me whether he meant it won’t scale to projects with large amounts of code, or large numbers of programmers, or large numbers of processes and/or threads, or something else entirely different.) He suggested we have a look at the video of him talking about this. Update: I thought that may be this YouTube video from Google Developer Day 2008 on “Python Design Patterns” (added June 4, 2007?), but Alex comments (below) that the video is not currently available, but we can find the slides at http://www.aleax.it/yt_pydi.pdf. And Alex described how the code would use “self” to refer to any dependency, which would allow that dependency to be changed by tests, or by other code that might use it.

I then asked a question (which I can no longer recall) and Alex stood up and came up to the microphone to elaborate. After sitting down, I (and others) asked further questions (which I also can’t recall) and Alex got some exercise sitting down and getting back up a few times until the two of us stayed at the podium and Alex gave me (and the audience) an impromptu (but very meaty) lesson on dependency injection. (I hope Google makes the video of this available—I’d like to watch it again myself. I haven’t found the video of my previous newbie nugget presentation yet though.) I do remember one (rather foolish) question I asked: I described how I had tried dependency injection using keyword arguments to functions with defaults, and how that gets complicated to test when testing a function which calls a function which calls a function with such an argument. Alex kindly repeated that dependency injection should be implemented using “self”, which I took to mean that one would use an object’s attributes to hold the dependencies. (This would certainly make the scenario I described much simpler to test.)

In a discussion after the meeting, I explained to one of several people I had very interesting follow-up conversations with (I didn’t get his name) on a white-board how this would work. The Google engineer who had graciously agreed at the last minute to host our meeting (I didn’t get is name either—but learned later that he was visiting from Australia) was watching (probably because we were over-staying our welcome) and as we were walking how he explained how the accepted technique is actually to use class attributes for the dependencies, since it’s less work to inject new dependencies into multiple objects, but one can still override dependencies for a particular object (since an object attribute will “replace” a class attribute with the same name).

One other question I recall (it may have been the last) was -jj’s. He asked (shrewdly) asked me to explain why one would want to use mocks. I decided to (figuratively) take a step back and first describe why one would want to test and use test-driven development (and what that is). I described the benefits those practices and then explained (or tried to) how mocks are used in testing to replace dependencies and allow unit tests to be truly “unit” tests and run fast to keep the test-implement-refactor cycle quick. TODO: Write this up in detail.

I concluded by thanking everyone for their questions and comments (especially Alex)—I stated I felt like I had learned more from everyone else than they probably did from me. When I sat down afterward, Alex turned to me and quoted Richard Bach: “You teach best what you most need to learn.”. Absolutely.

Next: Write up my experiences modifying my code and tests to use dependency injection, and contrast the new versions of the two examples from my slides.

2 comments:

Alex said...

Oops, sorry -- looks like the presentation I was thinking of is not currently available as video. You can get the slides at http://www.aleax.it/yt_pydi.pdf though.

Alex

Michael Foord said...

I respectfully disagree with Alex about dependency injection. :-)

Whilst it has its place when it improves your production code, using it purely for testing just unnecessarily pollutes your object signatures with dependencies - when they are perfectly testable without injection.

As for not scaling; well we have around 120 000 lines of test code at Resolver Systems and it is still scaling well enough for us. :-)

You should definitely try Mock 0.4. Unfortunately the interface for 'patch' has changed, but greatly for the better. :-)