Unit testing a modal dialog

Still working on automated testing of a wxPython application (called Transana).

I have a method that looks like this:

   def doImportDatabase(self, xmlFPath):

···

#
      # This clicks on the Import Database menu, which opens up
      # a modal dialog
      #
      self.widgetManip.chooseMenuItem(MenuSetup.MENU_TOOLS_IMPORT_DATABASE)

      #
      # This types a file name in the modal dialog, and clicks the OK button
      #
      importWndManip = self.xmlImportWndManip()
      importWndManip.doTypeXMLFileName(xmlFPath)
      importWndManip.doOK()

The problem I'm having is that as soon as the menu item is clicked, the modal dialog takes over, and the last three lines (entering the file name and clicking OK) do not happen
until I have manually clicked on the OK button to close the dialog box.

I tried running the application in a separate thread, than doImportDatabase, like this:

class TransanaInSeparateThread(threading.Thread):
   def __init__(self, app):
      threading.Thread.__init__(self)
      self.app = app
      
   def run(self):
      self.app.MainLoop()

   TransanaInSeparateThread(app).start()

   ... Then go about building the test harness and invoking doImportDatabase().

But to no avail. Does anyone have ideas for how I could get around this problem?

Thx

----
Alain Désilets, MASc
Agent de recherches/Research Officer
Institut de technologie de l'information du CNRC /
NRC Institute for Information Technology

alain.desilets@nrc-cnrc.gc.ca
Tél/Tel (613) 990-2813
Facsimile/télécopieur: (613) 952-7151

Conseil national de recherches Canada, M50, 1200 chemin Montréal,
Ottawa (Ontario) K1A 0R6
National Research Council Canada, M50, 1200 Montreal Rd., Ottawa, ON
K1A 0R6

Gouvernement du Canada | Government of Canada

     # This types a file name in the modal dialog, and clicks the OK button

                                        ^^^^^^^^^^^^

The problem I'm having is that as soon as the menu item is clicked, the
modal dialog takes over, and the last three lines (entering the file name
and clicking OK) do not happen until I have manually clicked on the OK
button to close the dialog box.

Alain,

   By definition, a modal dialog grabs the application until it is dismissed.
For testing purposes, you want the dialog to be in non-modal mode. Robin
posted the syntax a week or so ago; the archives will have that message.

HTH,

Rich

···

On Fri, 12 Jan 2007, Desilets, Alain wrote:

--
Richard B. Shepard, Ph.D. | The Environmental Permitting
Applied Ecosystem Services, Inc. | Accelerator(TM)
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863

Alain,

   By definition, a modal dialog grabs the application until
it is dismissed.
For testing purposes, you want the dialog to be in non-modal
mode. Robin posted the syntax a week or so ago; the archives
will have that message.

I couldn't find that message in the archive, but in any case, I don't think making the dialog non modal would work. Basically, the code that is invoked when the menu item is clicked has this:

       val = self.ShowModal()
       if val == wx.ID_OK:
          ... Process the inputs entered into the dialog

If I replace this by something like:

       val = self. Show()
       ... Process the inputs entered into the dialog

Then when I programmatically click on the menu item, the above code will be invoked, and control will return to my test script only once the processing of the dialog data has finished. Since my test code clicks on the menu item, AND THEN enters data into the dialog, it means that the data entered into the dialog will be NULL at the time when the dialog returns.

It seems to me that the application and the code that drives it for testing need to run in separate threads. But I tried that and it didn't work.

Actually, this brings me to the more generall question of:

"Has anybody else figured out exactly how to do automated acceptance testing of a wxPython application using PyUnit?"

I have been able to do *Unit* testing of individual dialogs. For example, I could test JUST the above dialog by starting it non modally, manipulating some of its components, and then seeing what happens as a response to it.

But now, I am trying to do global acceptance testing, where my test program does not control how the dialog is started.

Thx.

Alain Désilets

···

HTH,

Rich

--
Richard B. Shepard, Ph.D. | The
Environmental Permitting
Applied Ecosystem Services, Inc. | Accelerator(TM)
<http://www.appl-ecosys.com> Voice: 503-667-4517
Fax: 503-667-8863

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwidgets.org
For additional commands, e-mail:
wxPython-users-help@lists.wxwidgets.org

If I replace this by something like:

      val = self. Show()
      ... Process the inputs entered into the dialog

Then when I programmatically click on the menu item, the above code will
be invoked, and control will return to my test script only once the
processing of the dialog data has finished. Since my test code clicks on
the menu item, AND THEN enters data into the dialog, it means that the
data entered into the dialog will be NULL at the time when the dialog
returns.

   Could be. I've not used modeless dialogs. There should be no space in
self.Show(), but I assume that's a message typo and not in the code.

It seems to me that the application and the code that drives it for
testing need to run in separate threads. But I tried that and it didn't
work.

   Guess that a wxPython version of 'expect' is what's needed.

   Sorry that didn't help.

Rich

···

On Fri, 12 Jan 2007, Desilets, Alain wrote:

--
Richard B. Shepard, Ph.D. | The Environmental Permitting
Applied Ecosystem Services, Inc. | Accelerator(TM)
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863

   Guess that a wxPython version of 'expect' is what's needed.

   Sorry that didn't help.

No sweat. Thx for trying and have a good weekend.

Alain Désilets

Desilets, Alain wrote:

The problem I'm having is that as soon as the menu item is clicked,
the modal dialog takes over, and the last three lines (entering the
file name and clicking OK) do not happen until I have manually
clicked on the OK button to close the dialog box.

Break the test into multiple chunks. One that builds the dialog and another to test drive the dialog, and then another for things that need to be done when the dialog is completed. You then need to do something that will cause the middle chunk to be run after the dialog has been shown. Since I don't know how the rest of your testing framework works I can't really advise how to do that, but I would probably do something like use wx.CallAfter to cause the middle chunk to be invoked in the first idle time after ShowModal is called.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Desilets, Alain wrote:

Actually, this brings me to the more generall question of:

"Has anybody else figured out exactly how to do automated acceptance
testing of a wxPython application using PyUnit?"

OSAF has a framework for running functional tests of Chandler. It essentially does an automated test drive of the app, similar to what it sounds like you are attempting. It is quite complex however, and is tied very tightly to the structure and architecture of Chandler. There is some work going on right now to make it possible replace the current framework and to create tests by recording events and being able to play them back. It's taken some (minor) hacking of wxWidgets to make it possible, so I'm not sure yet if we'll be able to move that code into the official wxWidgets...

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Hi Alain,

Still working on automated testing of a wxPython application (called Transana).

I have a method that looks like this:

   def doImportDatabase(self, xmlFPath):
      #
      # This clicks on the Import Database menu, which opens up
      # a modal dialog
      #
      self.widgetManip.chooseMenuItem(MenuSetup.MENU_TOOLS_IMPORT_DATABASE)

      #
      # This types a file name in the modal dialog, and clicks the OK button
      #
      importWndManip = self.xmlImportWndManip()
      importWndManip.doTypeXMLFileName(xmlFPath)
      importWndManip.doOK()

The problem I'm having is that as soon as the menu item is clicked, the modal dialog takes over, and the last three lines (entering the file name and clicking OK) do not happen
until I have manually clicked on the OK button to close the dialog box.

I tried running the application in a separate thread, than doImportDatabase, like this:

class TransanaInSeparateThread(threading.Thread):
   def __init__(self, app):
      threading.Thread.__init__(self)
      self.app = app

   def run(self):
      self.app.MainLoop()

   TransanaInSeparateThread(app).start()

   ... Then go about building the test harness and invoking doImportDatabase().

But to no avail. Does anyone have ideas for how I could get around this problem?

There are no easy answers here, but you can read up on some (admittedly less-than-ideal) options at:

http://wiki.wxpython.org/index.cgi/Unit_Testing_with_wxPython?highlight=(testing)

BTW, much of wxWidgets is not thread safe, so you shouldn't run the wxPython mainloop in any thread other than the main thread.

Regards,

Kevin

···

On Jan 12, 2007, at 11:54 AM, Desilets, Alain wrote:

Thx

----
Alain Désilets, MASc
Agent de recherches/Research Officer
Institut de technologie de l'information du CNRC /
NRC Institute for Information Technology

alain.desilets@nrc-cnrc.gc.ca
Tél/Tel (613) 990-2813
Facsimile/télécopieur: (613) 952-7151

Conseil national de recherches Canada, M50, 1200 chemin Montréal,
Ottawa (Ontario) K1A 0R6
National Research Council Canada, M50, 1200 Montreal Rd., Ottawa, ON
K1A 0R6

Gouvernement du Canada | Government of Canada

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwidgets.org
For additional commands, e-mail: wxPython-users-help@lists.wxwidgets.org

The problem I'm having is that as soon as the menu item is clicked,
the modal dialog takes over, and the last three lines (entering the
file name and clicking OK) do not happen until I have manually
clicked on the OK button to close the dialog box.

Well, there's unit testing and there's acceptance testing. From the
rest of the thread, it sounds like you want to do acceptance testing
here, and I have no specific knowledge, but...

You already have a mechanism to click on a menu, it seems. In that
case, it seems like breaking the test code out into another thread
and having it send events to the main window may be just what the
doctor ordered. It'll require a bit more harness around the
unittest module, but I can imagine that you could probably put
together a fixture relatively easily that all of your tests could
then use.

If you want to do unit testing, however, test that you can open the
dialog, interact with its controls, and get the right values back.
Then test separately the database opening, parsing, etc. It's a bit
hard to see how to do this at first, but you'll get the hang of it
if you beat your head against your monitor for long enough. :slight_smile:

···

***

On the "fixture" bit, I do this a lot for testing wx stuff since
with frames opening and closing, and only sometimes being shown, wx
gets a bit hinky so the fixture makes sure that everything is
cleaned up appropriately.

Something along these lines:

class WindowFixture(unittest.TestCase):
  def setUp(self):
    self.app = wx.PySimpleApp()
    self.win = MyFrame.MyFrame()

  def tearDown(self):
    if self.win:
      self.win.Close()
    del self.app

  def Reveal(self):
    self.win.Show()
    self.app.MainLoop()
    self.win = None

...then all of my tests for "MyFrame" derive from this thing. I'd
actually toyed with making a ctor that took the class to be
instantiated, but it added a lot of noise for something I didn't
actually end up needing after carefully considering my problem.
(There's some documentation on the wxPyWiki about why you need to do
the above stuff, which I could expand on more if anyone cares.)
...and the Reveal method was stolen from an idea by Phlip that he
talks about in Test-First User Interfaces, where it's nice sometimes
just to inject a self.Reveal() into a test to show the current state
of the window.

-tom!

Thx for the hints.

Well, there's unit testing and there's acceptance testing.
From the rest of the thread, it sounds like you want to do
acceptance testing here, and I have no specific knowledge, but...

Yes, integrated Acceptance Testing is what I am trying to do.

You already have a mechanism to click on a menu, it seems.
In that case, it seems like breaking the test code out into
another thread and having it send events to the main window
may be just what the doctor ordered. It'll require a bit
more harness around the unittest module, but I can imagine
that you could probably put together a fixture relatively
easily that all of your tests could then use.

Good idea. This is sort of the inverse of what I tried when I ran the
wxPython app inside an other thread. You propose that it should instead
be the GUI driving code that is run in a separate thread. I'll try that.

If you want to do unit testing, however, test that you can
open the dialog, interact with its controls, and get the
right values back.
Then test separately the database opening, parsing, etc.
It's a bit hard to see how to do this at first, but you'll
get the hang of it if you beat your head against your monitor
for long enough. :slight_smile:

Yes, that's how I have done GUI testing in the past. But in the current
case, the application I am testing was not developed with that in mind.
The logic and presentation layers are very tightly coupled. I could of
course refactor it, but I would prefer not to, since the app does not
have automated testing in place (and I'm not reckless enough to do major
refactorings without a safety net).

But even with applications which I built to make this sort of piecemeal
testing possible, I have often been burned with bugs that only came up
when you considered a particular sequence of dialogs. So I think you
need to do this sort of Integrated Acceptance testing also, in addition
to the Piecewise Unit Testing.

Break the test into multiple chunks. One that builds the
dialog and another to test drive the dialog, and then another
for things that need to be done when the dialog is completed.

In my case, I'm dealing with an existing app where creation of the
dialog, displaying, and processing of the data entered into it are
bundled into one method. I could of course refactor to split them apart,
but I'm reluctant to do this without the safety net of a unit test suite
(the system has none at the moment).

But if all else fail, I will try that.

From: Desilets, Alain [mailto:Alain.Desilets@nrc-cnrc.gc.ca]
Actually, this brings me to the more generall question of:

"Has anybody else figured out exactly how to do automated
acceptance testing of a wxPython application using PyUnit?"

I have done so using nose. It's a Windows-only solution, but what I do is
use the AutoIt ActiveX control via win32com, which I use to drive the GUI
and make various assertions about its state.

Here's a simple one:

def testHelpDlg():
    """Tests that the help dialog appears when the
    menu shortcut keys are pressed. Assumes
    that the app has already been started."""

    oAutoIt = client.Dispatch("AutoItX3.Control")

    oAutoIt.Send("!hh") # Send Alt-h, h key combination

    # Test that Help dialog appears within 5 seconds
    assert 1 == oAutoIt.WinWaitActive("AnalyzeAssist Help",
                                          "",
                                          5)

    # dismiss the dialog
    oAutoIt.Send( "{ENTER}" )
    # allow 200 msec for dialog to disappear
    oAutoIt.Sleep( 200 )

AutoIt can also be used to click buttons, check the text in text boxes and
status bars, etc. You can also use it to record sequences of mouse and
keyboard work, although you will then need to translate the script it
produces into the COM version for use from Python.

Regards,
Ryan Ginstrom

Thx. Sounds useful.

···

-----Original Message-----
From: Ryan Ginstrom [mailto:ginstrom@tree.odn.ne.jp]
Sent: May 29, 2007 8:20 PM
To: wxPython-users@lists.wxwidgets.org
Subject: RE: [wxPython-users] Unit testing a modal dialog

> From: Desilets, Alain [mailto:Alain.Desilets@nrc-cnrc.gc.ca]
> Actually, this brings me to the more generall question of:
>
> "Has anybody else figured out exactly how to do automated
acceptance
> testing of a wxPython application using PyUnit?"

I have done so using nose. It's a Windows-only solution, but
what I do is use the AutoIt ActiveX control via win32com,
which I use to drive the GUI and make various assertions
about its state.
AutoIt Scripting Language - AutoIt

Here's a simple one:

def testHelpDlg():
    """Tests that the help dialog appears when the
    menu shortcut keys are pressed. Assumes
    that the app has already been started."""

    oAutoIt = client.Dispatch("AutoItX3.Control")

    oAutoIt.Send("!hh") # Send Alt-h, h key combination

    # Test that Help dialog appears within 5 seconds
    assert 1 == oAutoIt.WinWaitActive("AnalyzeAssist Help",
                                          "",
                                          5)

    # dismiss the dialog
    oAutoIt.Send( "{ENTER}" )
    # allow 200 msec for dialog to disappear
    oAutoIt.Sleep( 200 )

AutoIt can also be used to click buttons, check the text in
text boxes and status bars, etc. You can also use it to
record sequences of mouse and keyboard work, although you
will then need to translate the script it produces into the
COM version for use from Python.

Regards,
Ryan Ginstrom

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwidgets.org
For additional commands, e-mail:
wxPython-users-help@lists.wxwidgets.org