Showing posts with label Testing. Show all posts
Showing posts with label Testing. Show all posts

Saturday, January 28, 2012

CodedUI: Close Dialogs and Windows

UI automation could be a very very complex procedure. Several things could change between different executions, controls could be moved or renamed, etc.

Some simple things such as closing a dialog popup/window could be challenging..

Let’s try to recreate the problem. File/New Project in Visual Studio 2010, then select Windows Forms.

Add a button with the following handler on the click event:

MessageBox
private void button1_Click(object sender, EventArgs e)
{
    MessageBox.Show("Try closing me..", "Try closing me..", MessageBoxButtons.OKCancel);
}

Record a CodedUI test that opens our super simple WinForm App, click Button1 and then close the message box. Finally, add one last step to record that closes the Main Window.

Now run the previous CodeUI test and check how the test reports a successful execution. Note that your Message Box is still opened and your application is still waiting for input.

* I imagine that it should be a better way of working around this effect than the following. If you figure it out let me know :).

Let’s create a UIMapExtensions class (containing extensions methods of course)

UIMapExtensions
public static class UIMapExtensions
{
    public delegate void CloseTestControlCallback();

    public static bool CloseWindow(this UITestControl testControl, CloseTestControlCallback closeCallback)
    {
        var closeSuccess =
            testControl.WaitForControlCondition(
                tc =>
                {
                    closeCallback();
                    return !tc.Exists;
                }, 3000);
        return closeSuccess;
    }

    public static bool CancelDialog(this UITestControl testControl)
    {
        var closeSuccess =
            testControl.WaitForControlCondition(
                tc =>
                {
                    Keyboard.SendKeys("{ESC}");
                    return !tc.Exists;
                }, 3000);
        return closeSuccess;
    }

    public static bool AcceptDialog(this UITestControl testControl)
    {
        var closeSuccess =
            testControl.WaitForControlCondition(
                tc =>
                {
                    Keyboard.SendKeys("{ENTER}");
                    return !tc.Exists;
                }, 3000);
        return closeSuccess;
    }

    public static bool CloseWindow(this UITestControl testControl)
    {
        var closeSuccess =
            testControl.WaitForControlCondition(
                tc =>
                {
                    WinApi.PostMessage(tc.WindowHandle, WinApi.WM_CLOSE, 0, 0);
                    return !tc.Exists;
                }, 3000);
        return closeSuccess;
    }
}

I am using 3 techniques in the previous code fragment. In each one of them, we wait for the control to respond to the close action (for 3000 ms).

  1. CloseWindow that receives a delegate to the close action. You can pass in the original Close method generated by the UIMap as you will see in the Test Method code below.
  2. CloseWindow with no arguments using PostMessage Win API and P/Invoke (this is some how brute force :)).
  3. AcceptDialog and CancelDialog: assuming it is a message box with Ok/Cancel buttons and that the window is in the foreground.

Now you can just replace some of generated test code in the Test Class (not in the UIMap):

Test Method
[TestMethod]
public void CodedUITestMethod1()
{
    this.UIMap.OpenApplication();
    this.UIMap.OpenPopup();
    //this.UIMap.ClosePopup();

    // try one of the following..
    var closeSuccess = this.UIMap.UItryclosingmeWindow.CloseWindow(this.UIMap.ClosePopup);
    //var closeSuccess = this.UIMap.UItryclosingmeWindow.AcceptDialog();
    //var closeSuccess = this.UIMap.UItryclosingmeWindow.CancelDialog();
    //var closeSuccess = this.UIMap.UItryclosingmeWindow.CloseWindow();
    if (!closeSuccess)
        Assert.Fail("Popup not closed!");

    this.UIMap.CloseApplication();
}

My two cents..

Sunday, May 22, 2011

Load Test in Windows Azure with Visual Studio Test Agents

Tomorrow I will be presenting “Load Test in Windows Azure with Visual Studio Test Agents” on MSDN. Hope to see you there.

Register here.

Tuesday, April 26, 2011

Moles and Linq to SQL: Mocking Linq to SQL Behavior

In recent years there’s been major investments in developing tools in order to provide good quality assurance features and incorporate industry practices into them.

TDD, DDD, Unit Testing, Continuous Integration, etc.

Microsoft has recently (not so recently actually :)) ship some very nice features to help us improve our code quality by allowing us to create better tests over units of code.

Unit testing is all about proving individual units of code (positive and negative testing). Since a great deal of software is not always designed with this “unit independence” in mind, it can be a challenge to isolate dependencies for every piece of source code (specially legacy code).

Moles is an Isolation Framework that mocks/emulates the behavior of our components and external libraries, even for private members. You could even mock how a particular System.* class object in .NET behaves (check Pex & Moles site in Microsoft Research).

The following is a Repository object that uses Linq to Sql Data Context internally and doesn't provide a simple way of decoupling database access from its behavior. It means we cannot unit test the following class without a SQL database.

Customer Repository
public class CustomerRepository
{
    public IList<Customer> GetCustomers()
    {
        using (var db = new AdventureWorksDataContext())
        {
            return db.Customers.ToList();
        }
    }

    public Customer GetCustomer(int id)
    {
        using (var db = new AdventureWorksDataContext())
        {
            return db.Customers.FirstOrDefault(c => c.CustomerID == id);
        }
    }
}

By using Moles, we could replace inner behavior of this class.

Note: This is White Box Testing, meaning you need to have access to internal implementation in order to know how to mock it.

Mocking with Moles

Download and install Moles.

  1. Add Mole Assemblies for the project hosting the previous class (or the code you wish to test)
  2. Add Mole Assembly for System.Data.Linq
  3. Add Mole Assembly for System.Core

Check this tutorial about Pex & Moles for more information about the previous steps.

The following code fragment unit test our GetCustomers method:

Overriding Enumeration
[TestMethod()]
[HostType("Moles")]
public void GetCustomersTest()
{
    System.Data.Linq.Moles.MTable<Customer>.AllInstances.GetEnumerator =
        tc =>
        {
            var mockCustomers = new List<Customer>()
            {
                new Customer() { FirstName = "Javier" },
                new Customer() { FirstName = "Jorge" },
                new Customer() { FirstName = "Dario" }
            };

            return mockCustomers.GetEnumerator();
        };

    CustomerRepository target = new CustomerRepository();
    var customers = target.GetCustomers();
    Assert.IsNotNull(customers);
    Assert.IsTrue(customers.Count == 3);
    Assert.AreEqual("Javier", customers[0].FirstName);
}

Check out the use of Moles to specify the behavior of enumeration for the Table<Customer> (Customers) that Linq to Sql Provider uses to execute the query in the database. In the previous code, we avoid querying data from database and return an in-memory list of customers.

When executing a lambda expression using Linq to Sql, the source Linq provider must interpret the query and then issue a command to the database.

In order to intercept the query and return mock results, we must override default behavior for the expression evaluation logic of the Linq Provider.

Overriding Expression Eval
[TestMethod()]
[HostType("Moles")]
public void GetCustomerTest()
{
    var expectedCustomer = new Customer() { FirstName = "Javier", CustomerID = 1 };

    System.Data.Linq.Moles.MTable<Customer>.AllInstances.ProviderSystemLinqIQueryableget =
        tc =>
        {
            var qp = new System.Linq.Moles.SIQueryProvider();
            qp.ExecuteExpression01(exp => expectedCustomer);
            return qp;
        };

    CustomerRepository target = new CustomerRepository();

    var actualCustomer = target.GetCustomer(expectedCustomer.CustomerID);
    Assert.IsNotNull(actualCustomer);
    Assert.AreEqual(expectedCustomer.CustomerID, actualCustomer.CustomerID);
    Assert.AreEqual(expectedCustomer.FirstName, actualCustomer.FirstName);
}

The previous mocking code returns a Mock QueryProvider that does not evaluate the expression, it simply returns the in-memory customer reference.

I encourage you that read more about Moles here.

PS: This cool framework intercepts any managed operation by creating a CLR Host for the application.

Saturday, February 26, 2011

Visual Studio CodedUI Tests Search Criteria

CodedUI Test allows developers/testers to perform UI automated Tests over Windows desktop applications.

Theses kind of features are well documented over the web and several blogs.

In this post I’ll show how to modify generated CodedUI tests to overwrite UI elements search criteria.

This kind of test modifications are very frequent in some type of applications.

Some background..

CodedUI technology works over MSAA API (Microsoft Active Accesibility) and in order to automate user interaction with Windows, buttons, tabs, etc. (any interactive UI element), it needs first to find it and then perform one or more actions over it (this is Windows implementation for Assistive Technologies).

The initial finding or search process is a necessary step. The same way we search an element by seeing the screen, MSAA allows automatized applications to find elements in different ways.

Searching an UI element involves matching specific attributes against expected values. When all attributes or properties meet this “search criteria” a control is found and referenced from CodedUI test for its later used.

Sometimes, finding an UI element using an automated algorithm could be a non trivial task, since its properties or attributes change dynamically during the application lifecycle or between different executions (name, position, text, etc.).

Creating a CodedUI tests is done through a Visual Studio Wizard. It allows us to record our actions and then generating the proper source code (in VB.NET, C#, etc.). The generated source code is implemented using partial classes, thus allowing us to add custom behavior without modifying the auto generated file.

Example, finding a WPF button:

this.mUIInnerButton1Button.SearchProperties[WpfButton.PropertyNames.Name] = "Inner Button 1";

The SearchProperties collection contains an standard set of attributes used for searching a certain kind of control (in this case, a WPF Button).

We can always change and pass the values we decide to this property bag or collection (overwriting the generated default behavior). However, finding a control sometimes require us to perform more elaborated procedures.

Overwriting Control Definition and Search Criteria..

This means, modifying the generated code to change default search criteria logic.

We do not modify .Designer.cs file. We add our new search criteria in the .cs file associated with the UIMap visual studio element:

image

Let´s work over the next sample. Suppose we have defined a WPF button with dynamic text (current DateTime for instance, only for demo purposes). The following XAML is the static content of our main window application.

Code Snippet
<Grid>
    <TabControl>
        <TabItem Header="Tab 1">
            <Button>
                <ContentControl>
                    <StackPanel x:Name="ButtonStackPanel">
                        <TextBox>Text 1</TextBox>
                        <TextBox>Text 2</TextBox>
                        <Button Click="Button1_Click">Inner Button 1</Button>
                        <Button Click="Button2_Click">Inner Button 2</Button>
                        <ComboBox>
                            <ComboBoxItem Content="Item 1" IsSelected="True" />
                            <ComboBoxItem Content="Item 2" />
                            <ComboBoxItem Content="Item 3" />
                        </ComboBox>
                    </StackPanel>
                </ContentControl>
            </Button>
        </TabItem>
        <TabItem Header="Tab 2" />
        <TabItem Header="Tab 3" />
    </TabControl>
</Grid>

Then, by code we create a third button with dynamic text on it:

Code Snippet
public MainWindow()
{
    InitializeComponent();
    this.Loaded += (s, e) =>
    {
        var dynamicButton =
            new Button()
                {
                    Content = "Dynamic Button: " + DateTime.Now.ToString(),
                };
        dynamicButton.Click += (sender, args) => MessageBox.Show("Dynamic Button Click");
        this.ButtonStackPanel.Children.Add(dynamicButton);
    };
}

If your record actions using CodedUI wizard you will get something similar to the following search criteria:

this.mUIDynamicButton2602201Button = new WpfButton(this);
#region Search Criteria
this.mUIDynamicButton2602201Button.SearchProperties[WpfButton.PropertyNames.Name] = "Dynamic Button: 26/02/2011 06:22:29 p.m.";
this.mUIDynamicButton2602201Button.WindowTitles.Add("MainWindow");
#endregion

Try running the test and you’ll end up with an error on the button click (note the name attribute contains the current DateTime, thus every run will generate a different button name). This was expected of course.

So, lets modify the generated UI test code to find the control. In the UIMap.cs (non designer file), add the following code:

Code Snippet
public partial class UIMap
{
    public UIMap()
    {
        this.UIMainWindowWindow.UIItemTabList.UITab1TabPage.Find();
        var tabItemStackPanel = this.UIMainWindowWindow.UIItemTabList.UITab1TabPage.GetChildren()[0];
        var dynamicButton =
            tabItemStackPanel.GetChildren().FirstOrDefault(c => c.Name.Contains("Dynamic"));
        this.UIMainWindowWindow.UIItemTabList.UITab1TabPage.UIDynamicButton2602201Button.CopyFrom(dynamicButton);
    }
}
  1. The custom code is added in the constructor of the UIMap, but you can easily create an Init() method (recommended) and then call it before invoking the test.
  2. We first need to find the control by searching the children of the parent StackPanel (note in the XAML that the StackPanel is the first and only child of the TabItem).
  3. Once we’ve found the StackPanel we enumerate its children, by filtering the results by control name (we are looking for something that begins with the word “Dynamic”).
  4. Finally, we need to clear the recorded search properties values and add the correct name filter value. You can do this by copying the button’s definition (with the CopyFrom() method). This copies all properties from the just found button to the one used by the test.

Final notes..

  • This is a simple example to show how you can manually search and iterate through the entire UI structure of an application to find elements. You can do this by position, name, display text, control type, etc.
  • You can implement the same behavior to find any control in the hierarchy.
  • This API could be used to automate applications for any purpose (not only for tests).