Click here to Skip to main content
15,891,375 members
Articles / Desktop Programming / Windows Forms

UITestBench, a lightweight UI testing library

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
3 Apr 2008CPOL9 min read 53.1K   226   41  
This article describes how to build a lightweight test bench for testing user interfaces which are written entirely in C#/.NET, using NUnit or any other unit test framework.
/*
 * This file is licensed under the Code Project CPOL License
 * http://www.codeproject.com/info/cpol10.aspx
 * 
 * � Steffen Sch�tte 2008
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Threading;

namespace de.steffenschuette.UITest.Framework
{
    public class UITestBench
    {
        private Thread uiThread;
        private UIScanner scanner;


        public UITestBench()
        {
            scanner = new UIScanner();
        }

        /// <summary>
        /// Starts the application.
        /// </summary>
        /// <param name="assemblyName">Name of the assembly.</param>
        /// <param name="args">The args.</param>
        public void StartApplication(string assemblyName, object args)
        {
            Assembly assembly = Assembly.Load(assemblyName);

            if (assembly != null)
            {
                //Invoke the application under test in a new STA type thread
                uiThread = new Thread(new ParameterizedThreadStart(this.Execute));
                uiThread.TrySetApartmentState(ApartmentState.STA);
                uiThread.Start(new ApplicationStartInfo(assembly, args));
            }
            else
            {
                throw new Exception("Assembly '" + assemblyName +"' not found!");
            }
        }

        /// <summary>
        /// Executes the specified param.
        /// </summary>
        /// <param name="param">The param.</param>
        private void Execute(object param)
        {
            Console.WriteLine("UIExecute ThreadId: " + Thread.CurrentThread.ManagedThreadId);

            Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);

            ApplicationStartInfo startInfo = (ApplicationStartInfo)param;

            Assembly ass = startInfo.AssemblyToStart;
            if (ass.EntryPoint.GetParameters().Length == 1)
            {
                ass.EntryPoint.Invoke(null, new object[] { startInfo.Arguments });
            }
            else
            {
                ass.EntryPoint.Invoke(null, new object[] { });
            }
        }

        /// <summary>
        /// Handles the ThreadException event of the application under test. I.e. any uncought exceptions within the application.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Threading.ThreadExceptionEventArgs"/> instance containing the event data.</param>
        private void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
        {
            Console.Error.WriteLine("The test application thread threw an exception and is terminating...");
            Application.Exit();
        }


        /// <summary>
        /// Terminates the application.
        /// </summary>
        public void TerminateApplication()
        {
            Console.Write("Forcing application to shut down if it has not terminated already....");
            if (uiThread != null && uiThread.IsAlive)
            {
                Console.WriteLine("Done!");
                uiThread.Abort();
            }
            else
            {
                Console.WriteLine("not required!");
            }
        }

        /// <summary>
        /// Triggers a scan for UI elements.
        /// </summary>
        public void ScanForUIElements()
        {
            this.scanner.ScanUIElements();
        }

        #region UIActions

        //Set selected index
        private delegate void SetSelectedIndexDelegate(Object element, int index);

        private void SetSelectedIndexEvent(Object element, int index)
       {
            element.GetType().GetProperty("SelectedIndex").SetValue(element, index, null);
        }

        private void SetSelectedIndex(UIElementInfo elementInfo, int index)
        {
            //Delegate based invocation is used to prevent a Illegal Cross Thread Exceptions under some circumstances
            InvokeOnOwningForm(elementInfo, new SetSelectedIndexDelegate(SetSelectedIndexEvent), index);
        }

        
        public void SetSelectedIndex(String formKey, String elementKey, int index)
        {
            try
            {
                SetSelectedIndex(scanner.GetUIElement(formKey, elementKey), index);
            }
            catch (KeyNotFoundException)
            {
                throw new KeyNotFoundException("Form: '" + formKey + "' / Item: '" + elementKey + "'");
            }    
            
        }

        //Set text
       private delegate void SetTextDelegate(Object element, string text);

        private void SetTextEvent(Object element, string text)
        {
            element.GetType().GetProperty("Text").SetValue(element, text, null);
        }

        private void SetText(UIElementInfo elementInfo, string text)
        {
            //Delegate based invocation is used to prevent a Illegal Cross Thread Exceptions under some circumstances
            InvokeOnOwningForm(elementInfo, new SetTextDelegate(SetTextEvent), text);
        }

        public void SetText(String formKey, String elementKey, string text)
        {
            try
            {
                SetText(scanner.GetUIElement(formKey, elementKey), text);
            }
            catch (KeyNotFoundException)
            {
                throw new KeyNotFoundException("Form: '" + formKey + "' / Item: '" + elementKey + "'");
            }    
        }


        //Perform Click
        private delegate void PerformClickDelegate(Object element);

        private void PerformClickEvent(Object item)
        {
            item.GetType().GetMethod("PerformClick").Invoke(item, null);
        }

        private void PerformClick(UIElementInfo elementInfo)
        {
            //Delegate based invocation is used to prevent a Illegal Cross Thread Exceptions under some circumstances
            BeginInvokeOnOwningForm(elementInfo, new PerformClickDelegate(PerformClickEvent));
        }

       
        public void PerformClick(String formKey, String itemKey)
        {
            try
            {
                PerformClick(scanner.GetUIElement(formKey, itemKey));
            }
            catch (KeyNotFoundException)
            {
                throw new KeyNotFoundException("Form: '"+formKey+"' / Item: '"+itemKey+"'");
            }
        }


        private void InvokeOnOwningForm(UIElementInfo itemInfo, Delegate theDelegate, object index)
        {
            ((Form)itemInfo.OwningForm).Invoke(theDelegate, itemInfo.Element, index);
        }

        private void BeginInvokeOnOwningForm(UIElementInfo itemInfo, Delegate theDelegate)
        {
            ((Form)itemInfo.OwningForm).BeginInvoke(theDelegate, itemInfo.Element);
        }

        #endregion


    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Germany Germany
I currently hold the following qualifications

- PhD in Computer Science
- M.Sc. in Software Technology
- Diplom (FH) in Computer Science

Comments and Discussions