My Blahg

April 3, 2007

NUnitForms and Modal Dialogs

Filed under: c#, dotnet, Uncategorized — treyhutcheson @ 12:35 pm

Two weeks ago I transitioned to another project at work. This project is an automated test harness for an api. The api that is being tested is written in c#. It wraps access to a web service, and provides dialogs for selecting inputs to the web service. This api is an integration layer that is exposed as a set of reverse com interop objects, and is consumed from a legacy Delphi application.

The test harness is used to automate integration tests. It will use the api to make calls against the web service. But to make sure that the requests being sent to the web service are actually coming from the user interface rather than being programmatically generated via the api on an object level, the test harness must drive the user interface itself programmatically.

I came to the project rather late, so the toolset had pretty much been chosen for me. To drive the UI, we’re using the alpha 5 release of NUnitForms 2. This is my first encounter with NUnitForms, and I dig it. I gave up on writing GUI’s years ago, except where absolutely necessary, so I never had any interest in NUnitForms. Now that I’ve used it, I must say that it offers a great amount of utility.

Being an alpha release, I’ve run into a few problems. For the most part, I’ve been able to get around them by changing my approach. However, I encountered an issue that really threw me for a loop.

All of the dialogs exposed through the api are modal. A few of the dialogs are only launched from other modal dialogs. So to get to dialog B, one must first launch Dialog A and click on a button on Dialog A. NUnitForms provides a mechanism for calling back into test cases after a modal dialog has been displayed: the ModalDialogTester class. This class has a public method named ExpectModal, which takes the name of the form to watch for, and a delegate that is used as a callback after the dialog has been displayed.

This mechanism has worked for the most part, except for those cases where a modal dialog launches another modal dialog. I would encounter an AmbiguousNameException, stating that more than one form was present with the same name. What makes the situation so weird is that if I put a breakpoint anywhere between the display of the first modal dialog and the second, I would not receive the exception. When I ran the application outside of the debugging environment, there would be no exception. So I just decided to live with it while debugging.

I was wrong. For a single test script, there was no AmbiguousNameException outside of the debugging environment. But if I processed more than one test script in batch, I would get the exception. I beat my head against this issue for a solid two days. I couldn’t find the source code for any revision of NUnitForms 2, so I just downloaded the source code for version 1.3.1. Looking into the code, the ModalFormTester class internally makes use of the FormFinder class.

Now the FormFinder class is interesting. The FindAll method accepts a string(the name of the form to find), and uses the Win32 API to enumerate all top-level windows(those windows underneath the result of the GetDesktopWindow api). Inside the windows enumeration callback, the FormFinder calls Control.FromHandle(hwnd) to get a reference to the enumerated handle as a WinForms control. This method is static, and I didn’t know it even existed. If it’s not a WinForms control, the result is null. So the result is cast as a Form, and if it isn’t null, the form’s name property is compared against the name argument passed in to the FindAll method. If the name matches, the form instance is added to a collection. The collection is returned from FindAll.

The Find method(singular) internally calls the FindAll method. If no forms are found, it throws a NoSuchControlException. If more than one form is found, it throws an AmbiguousNameException. So I was able to track down where the exception is being thrown, but I couldn’t figure out the condition causing more than one form with the same name to be found. I know there’s only one being instantiated and displayed.

After a few days of futility, I decided that enough was enough. Maybe this wouldn’t be an issue if we weren’t using an alpha build, but that’s out of my control. To solve my problem, I implemented two new classes: CustomFormFinder and ModalFormListener. CustomFormFinder effectively duplicates the logic of the original FormFinder class, except it doesn’t throw any exceptions. It is up to the caller to determine if zero, or more than 1 forms, is an exceptional circumstance. One improvement that I made is that the methods are all static, possible due to dotnet 2’s ability to define delegates anonymously. This way I can have my Windows Enumeration method implemented inline inside of the parent method; the benefit here is that the class is now completely stateless. Another improvement is that I added genericized overloads to both FindForms and FindSingleForm. The generic overloads don’t compare against form name; rather they find all forms of a given type. For example, the method signature of the genericized FindSingleForm looks like this:

public static T FindSingleForm<T>() where T : Form;

After I created and tested the CustomFormFinder class, I implemented the ModalFormListener class. This class has a series of overloaded static methods named RegisterModalCallback. These methods will invoke a callback after a modal form has been displayed. The overloads either work off of form name, or form type T. Like the CustomFormFinder, the methods are static, so all state has been eliminated.

Now the primary difference between my ModalFormListener and NUnitForms’s ModalFormTester is the way I look for forms. I couldn’t find the source code for the ModalFormTester::ExpectModal method, so I’m shooting in the dark here. I assume that it’s using system hooks to be capture any ACTIVATE messages. When a matching form is found, the callback is fired. In the case of my ModalFormListener, I simply fire off a timer into a callback method. Each timer internal I call CustomFormFinder to find the requested form, and if it’s found, I disable the timer and invoke the callback.

It works, and the best part is that I don’t get any unpredictable behavior. And one benefit of using the timer is that the timer event handler is invoked on the main thread, which means that the callback is itself invoked on the main thread.

4 Comments »

  1. Hi, I have recently struggled with similar Ambiguous exceptions during NUnitForms 2 (alpha 5) testing myself. Would it be possible for you to provide me with your extensions to the NUnitForms framework? It would be much appreciated :o) Best regards, Kim

    Comment by Kim Hauge — January 21, 2008 @ 3:59 am

  2. Hi,

    NUnitForms v2.0 alpha5.

    I had the same issues with Ambiguous exceptions, but I probably fixed them in my code.
    Please add another parameter to your tester method: your Form name.

    public class ToolStripButtonTester : ToolStripItemTester
    {
    public ToolStripButtonTester(string name);
    public ToolStripButtonTester(string name, Form form);
    }

    My previous declaration was:
    ToolStripButtonTester btnViewArch = new ToolStripButtonTester(“tsbViewArch”);

    My NEW declaration is now:
    ToolStripButtonTester btnViewArch = new ToolStripButtonTester(“tsbViewArch”, LogBrowser.ActiveForm);

    Hope this help other people.

    Chris

    Comment by Chris Lessard — October 21, 2008 @ 10:32 am

  3. can you help me test unsupported forms of NUnitForms like file dialog etc. Also my testing for save dialog doesn’t work, I hope you can help me. I need it badly. tnx. Pls email me the details.

    Comment by Aimee — April 14, 2009 @ 8:11 pm

  4. What if there are two instances of the same form. How can I distinguish between the two instances?

    Comment by yossi greenbaum — March 9, 2010 @ 1:39 pm


RSS feed for comments on this post. TrackBack URI

Leave a comment

Create a free website or blog at WordPress.com.