[TIP] best strategy for retrofitting code with unit tests
fuzzyman at voidspace.org.uk
Mon Mar 5 15:21:47 PST 2007
Nate Lowrie wrote:
> On 3/5/07, Michael Foord <fuzzyman at voidspace.org.uk> wrote:
>> Nate Lowrie wrote:
>> > Ok, I have done a couple of small - medium sized projects through a
>> > TDD cycle and have been successful at it. Now, we are trying to
>> > retrofit a large framework (Dabo) with unit tests. It's a 3 tier
>> > framework with an agnostic db layer, business object/logic layer, and
>> > a UI layer that currently wraps WxPython.
>> > What is the best strategy for this? Should we just adopt a TDD for
>> > all new code and do a refactor/red/green cycle on the existing code as
>> > the new code comes in? Should we unit test the business layer only
>> > and resign to acceptance testing on the other 2?
>> NB I've not had to *do* this, so my suggestion is theoretical only.
>> I would add acceptance tests for the higher levels as soon as you can.
> Any suggestions for acceptance testing the UI layer? It is a wrapper
> over wx that makes th UI more pythonic. It also constitutes about 60%
> of the code base. I remember seeing something about pywinauto but
> wasn't quite sure if that was for window forms or another toolkit.
pywinauto is for general Windows automation - so it may well be what you
For Windows Forms we hit the win32 api directly for automation. This is
easy because exposing the win32 api to IronPython code with the use of a
bit of C# is trivially easy.
You can probably get the same effect using the pywin32 extensions by
Mark Hammond. The relevant stuff you need is almost all in User32.dll.
You might find that pywinauto or pyAA expose most of this already.
To test the results our functional test framework gives us access to the
controls we need (text boxes etc) so that we can test their state.
We have a class called 'FunctionalTest' which is a subclass of
'unittest.TestCase'. This exposes methods like 'getMainTextBox'. The
setUp of FunctionalTest launches the application.
We can then do (trivial example) :
textbox = self.getMainTextBox()
We have convenience methods to move the mouse to the centre of a GUI
component and do a left click or a right click. (Or a drag operation or
whatever - simulating user input.)
There is a bit of added complexity [#]_ because the GUI is inevitably
running on another thread - so to access live properties of GUI
components you probably need to invoke calls onto the GUI thread.
Windows Forms makes this easy, you would need to see how wx does this.
It is quite a bit of work to get a good functional test framework in
place - but well worth the effort.
We've put quite a lot of work into our convenience functions (including
custom asserts) - so the calls to simulate user action are effectively a
DSL. We have actually considered creating a mini-specification language
for our user stories, which could be automatically be translated into
actions and tests.
The user clicks the red button
The scary dialog appears with the message "don't press the red button"
Could be translated to :
scaryDialog = self.dialogManager().getScaryDialog()
self.assertDialogVisible(scaryDialog, message="don't press the red button")
All the invokes are done 'behind the scenes', so I assert that our
functional tests (built on the unit test framework) are generally very
All the best,
.. [#] Plus further complexity because SendKeys seems to be unreliable.
We have a version which checks that the keypreses actually arrive and
resends ones that are dropped. *sigh*
More information about the testing-in-python