Tuesday, October 13, 2009

UI Automation on Prism Apps using project White


White project is a very interesting UI testing framework for WPF applications. White is open-source, written in C# and it supports all rich client applications, which are Win32, WinForm, WPF and SWT (java). It provides a consistent object oriented API and it hides all the complexity of Microsoft's UI Automation library and Win32 Windows messages.  Before using White it’s good to have Windows SDK installed so that you have the UI Spy tool which is a standalone executable which enables developers to view all UI elements and their details. UISpy helps you to get the visual of a UI Automation tree with root element that represents the current desktop and the child elements that represent application windows. Each of these child elements contains UI elements such as menus, buttons, radio buttons, textboxes, toolbars, list boxes etc.
I used white to automate UI testing for my Prism application. One of the major activities I had to do before writing the test methods was to make the application modules and shell assemblies available in the test output directory. You can set the deployment options for your VSTS projects by opening the LocalTestRun.testrunconfig file in visual studio.


 
Once you have setup the deployment options you can start working on the test methods to automate the UI testing. Bil Simser has a very good sample on creating a wrapper class for White which has a Disposable pattern implementation. I have used the wrapper class from Mr. Simser’s blog for my test class.
Automation starts by calling the static Launch() method of the White Application class. GetWindows() has to be called to get a list of all opened windows. By using search criteria and the generic Get<>() method of a window it is quite easy to access buttons, textboxes, menus etc. The Keyboard singleton can be used to send keystrokes to the focused control or a window.
My WhiteWrapper class is implemented as
public class WhiteWrapper : IDisposable
{
    private readonly Application ___Host = null;
    private readonly Window ___MainWindow = null;

    public Window MainWindow
    {
        get { return ___MainWindow; }
    }


    public WhiteWrapper()
    {
        ___Host = Application.Launch(Constants.ShellPath);
    }

    public WhiteWrapper(string windowTitle)
        : this()
    {
        ___MainWindow = GetWindow(windowTitle);
    }

    public Window GetWindow(string windowTitle)
    {
        return ___Host.GetWindow(windowTitle, Core.Factory.InitializeOption.NoCache);
    }

    public T GetControl(string controlName) where T : UIItem
    {
        if (___MainWindow == null)
            throw new ArgumentException("Main window not initialized");
        return ___MainWindow.Get(controlName);
    }

    public T GetControl(int index) where T : UIItem
    {
        if (___MainWindow == null)
            throw new ArgumentException("Main window not initialized");
        return ___MainWindow.Get(SearchCriteria.Indexed(index));
    }

    public T GetControl(string controlName, Window window) where T : UIItem
    {
        if (window == null)
            throw new ArgumentNullException("window");
        return window.Get(controlName);
    }

    public T GetControl(Window window, int index) where T : UIItem
    {
        if (window == null)
            throw new ArgumentNullException("window");
        return window.Get(SearchCriteria.Indexed(index));
    }   

    public T GetControlByText(string text) where T : UIItem
    {
        if (___MainWindow == null)
            throw new ArgumentException("Main window not initialized");
        return ___MainWindow.Get(SearchCriteria.ByText(text));
    }

    public T GetControlByText(string text, Window window) where T : UIItem
    {
        if (window == null)
            throw new ArgumentNullException("window");
        return window.Get(SearchCriteria.ByText(text));
    }

    public T GetControlByText(string text, int index) where T : UIItem
    {
        if (___MainWindow == null)
            throw new ArgumentException("Main window not initialized");
        return ___MainWindow.Get(SearchCriteria.ByText(text).AndIndex(index));
    }

    #region IDisposable Members

    public void Dispose()
    {
        if (___Host != null)
            ___Host.Kill();
    }

    #endregion
}
In my test methods I can use the methods from the WhiteWrapper
[TestMethod]
public void If_valid_employee_code_is_supplied_search_results_should_be_displayed()
{
    using (WhiteWrapper wrapper = new WhiteWrapper(___MainWindowName))
    {
        var __SearchTextBox = wrapper.GetControl<TextBox>("txtSearch");
        __SearchTextBox.Text = "001AA";
        var __SearchButton = wrapper.GetControl<Button>("btnSearch");
        __SearchButton.Click();
        var __Expander = wrapper.GetControl<Button>("HeaderSite");
        Assert.IsNotNull(__Expander);
        Assert.IsTrue(__Expander.Visible);
    }
}


No comments: