Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Automated Test System: Part I

3.13/5 (5 votes)
17 Mar 2009GPL32 min read 31.2K   477  
The article attempts to demonstrate a way of testing in the .NET framework. This is just a demonstration of how we can do automated testing in C# .NET.

Introduction

This article presents just a demonstration of how we can start an automated test system development. It's open to all to contribute and make it more worthy. Also, kindly note that you will require both the ATS.zip and Testapp.zip.

Background

I had been looking for an automated test system tool for quite some time, but could not find something which was reliable. So I thought why not try and develop something myself.

main.png

Using the code

The code can be used by anyone who has a basic understanding of the Reflection namespace in the .NET framework. The sole idea is based on any other reflector available in the market, for example Reflector, or the ILDASM that ships with the installation of the .NET framework.

I have tried to use the same idea here. Most of the usable code resides in ATSMainForm.cs. Once we are inside the application:

C#
/// ********************************************************************
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main ()
{
    Application.Run (new ATSMainForm ());
}

We will need to select the target assembly/executable and hit on Launch. What we do in launch is shown here:

C#
///**********************************************************************
/// <summary>
/// Click event handler for the launch button.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="ea">An <see cref="EventArgs"/> object
/// that contains the event data.</param>
/// <remarks>
/// Launches the target application selected via browse button.
/// </remarks> 
private void launchBtn_Click (object sender, System.EventArgs ea)
{
    // Enable/Disable controls
    this.m_mainTv.Nodes.Clear ();
    this.m_mainPnl.Enabled = true;
    this.m_invokeMethodBtn.Enabled = false;
    this.m_methodNameTxtBox.Enabled = false;

    try
    {
        this.m_testAssembly = Assembly.LoadFrom (this.m_appPathTxtBox.Text);
        // Get the available types (controls)
        Type[] types = m_testAssembly.GetTypes ();

        // Try resolving the types and add relevent ones to the treeview
        ResolveTypes (types);

        // Find the entry method and launch the application.
        // TODO: If the default constructor is private, we need to find out
        // how the application can still be launched.
        MethodInfo entryMethod = m_testAssembly.EntryPoint;

        this.m_testForm = m_testAssembly.CreateInstance (
            entryMethod.DeclaringType.FullName,
            true,
            this.m_bindingFlags,
            null,
            null,
            System.Globalization.CultureInfo.InvariantCulture,
            null);
        // Constructor created, try launching??
        ThreadPool.QueueUserWorkItem (new WaitCallback (RunApp), m_testForm);
    }
    catch (Exception ex)
    {
        {
            Form errForm = new ATSError (ex);
            errForm.ShowDialog ();
        }
    }
} // end launchBtn_Click

The good thing about Reflection can be observed here. .NET Reflection allows you to list down all methods/properties/events/classes .. in fact, every thing that is related to this application.

Here is how:

C#
//**********************************************************************
/// <summary>
/// Tries resolving the methods and adds relevent ones to the treeview.
/// <param name="types"> An array of types defined in the loaded assembly
/// </param>
/// <param name="nodeRank"> The index of current <see cref="TreeNode"/>
/// </param>
///</summary>
private void AddMethods (int nodeRank, Type[] types)
{
    MethodInfo[] methods = types[nodeRank].GetMethods (m_bindingFlags);
    this.m_mainTv.Nodes[nodeRank].Nodes.Add ("Methods");

    this.m_mainTv.Nodes[nodeRank].Nodes[1].ImageIndex = 2;
    this.m_mainTv.Nodes[nodeRank].Nodes[1].SelectedImageIndex = 2;

    this.m_tsStatus.Text = "Analysing Methods...";
    this.m_tsProgressBar.ProgressBar.Maximum = methods.Length;

    int nodeCount = 0;

    for (int iMethodCount =0; iMethodCount < methods.Length; iMethodCount++)
    {
        this.m_tsProgressBar.ProgressBar.Value = (int) (iMethodCount/methods.Length * 100);
        this.m_tsStatus.Invalidate ();
        MethodInfo method = (MethodInfo) methods.GetValue (iMethodCount);
        MethodAttributes methodAttrib = method.Attributes;
        if ((method.DeclaringType.FullName == types[nodeRank].FullName.ToString ()) && 
                !(methodAttrib.ToString ().Contains ("SpecialName")))
        {
            this.m_mainTv.Nodes[nodeRank].Nodes[1].Nodes.Add (method.ToString ());
            this.m_mainTv.Nodes[nodeRank].Nodes[1].Nodes[nodeCount].ImageIndex = 2;
            this.m_mainTv.Nodes[nodeRank].Nodes[1].Nodes[nodeCount].SelectedImageIndex = 2;
            this.m_mainTv.Nodes[nodeRank].Nodes[1].Nodes[nodeCount].Tag = method;
            nodeCount++;

        }
    }

    this.m_tsProgressBar.ProgressBar.Value = 0;

} // end AddMethods

Similarly, the events and properties/fields can also be listed. This is one of the approaches that can be followed to write your own reflector or ILDASM :)

testapp.png

Next, the user can start clicking on the method names by which the invoke method textbox is enabled/disabled and then we can invoke any method (say button1_click) as demonstrated in testapp.zip.

Points of interest

pass.png

An interesting point is the way the test is run. Right now, the method names are hard-coded, but later we might like to have these read from some Excel file or something and then use those method names instead.

History

  1. Modified the code a bit to have the start-up form input from the user. The name of the form will be its fully qualified name, i.e., NameSpaceName.FormName.
  2. Check out the latest binaries. I'll upload the updated code and documentation very soon.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)