Coded UI test automation of MVC applications with Visual Studio

Coded UI test automation of MVC applications with Visual Studio

Whether you are a software developer, tester, administrator or analyst, this article can help you master different types of UI testing of an MVC application, by using Visual Studio for creating coded UI test suites that can be automated for continuous execution.

 

Coded UI Tests

Automated tests that drive your application through its user interface (UI) are known as coded UI Tests. These tests include functional testing of the UI controls. They let you verify that the whole application, including its user interface, is functioning correctly. Coded UI Tests are particularly useful where there is validation or other logic in the user interface, for example in a web page. They are also frequently used to automate an existing manual test.

As shown in the following illustration, a typical development experience might be one where, initially, you simply build your application and click through the UI controls to verify that things are working correctly. You then might decide to create a coded test so that you don’t need to continue to test the application manually. Depending on the particular functionality being tested in your application, you can write code either for a functional test, or for an integration test that might or might not include testing at the UI level. If you simply want to access directly some business logic, you might code a unit test. However, under certain circumstances, it can be beneficial to include testing of the various UI controls in your application. A coded UI test can automate the initial scenario, verifying that code churn does not impact the functionality of your application.

codedui_testing

Manual vs. Coded testing types.

 

The MVC Application in test

This is the first of a series of articles about coded UI test automation. The first scenario being analysed is about MVC web applications. The application in test is pretty straightforward, consisting of a number of screens (web pages) for listing, creating, editing and deleting pseudo-users. As said, the technology in use is ASP.NET MVC 5. Additional frameworks include: Entity Framework as ORM with SQL Server as database, Bootstrap for responsive design, JQuery UI for a date picker component. Let’s analyse more in detail the Model, Controller and View of this application, before digging into the coded UI test project.

 

The Model

The application in test performs simple CRUD operations on a User model. We define our model with the following properties:

·         Id: Unique identifier of the user; that’s the primary key at database level.

·         FirstName: First name of the user; this is a required field.

·         LastName: Last name of the user; this is a required field.

·         Email: Email address of the user; this is required and also unique, that is there cannot be two users with the same email address.

·         DateOfBirth: Date of birth of the user; this field is optional.

 

To enforce uniqueness of the email address, we can use the Index attribute on the Email property, specifying IsUnique = true. This attribute is defined in the System.ComponentModel.DataAnnotations.Schema namespace, and we need a reference to Entity Framework in our project. To add an index to a string field, we also need to set a length (indexes cannot be applied to unbounded strings), so we can use the StringLength attribute as well. Lastly, we can decorate the Email property with the DataType attribute of type EmailAddress, so we have it formatted as a “mailto:” link automatically in our views, and we get HTML5 validation of the email address in the browser.

DateOfBirth instead is an optional field, and because we are using the DateTime type, which is a struct in C#, thus a value type, thus cannot be null, we need to make it nullable in order to accept null values. And we also decorate it with the Date DataType attribute (just Date, no Time, no need to capture the time of birth!)

The source code for the User class is very straightforward:

public class User

{

    public int Id { get; set; }

 

    [Required]

    [DisplayName("First Name")]

    public string FirstName { get; set; }

 

    [Required]

    [DisplayName("Last Name")]

    public string LastName { get; set; }

 

    [Required, Index(IsUnique = true)]

    [StringLength(200)]

    [DataType(DataType.EmailAddress)]

    public string Email { get; set; }

 

    [DataType(DataType.Date)]

    [DisplayName("Date of Birth")]

    public DateTime? DateOfBirth { get; set; }

}

The User model.

 

The View

We’ll focus on the Create view for the purpose of this article. The generation of the views follows the MVC automatic scaffolding when adding a new Controller with reads/write Entity Framework operations.

The Create view, obviously, presents all the fields in a form, with a bit of Bootstrap here and there to make it look nice with little effort J

For the Date of Birth field, also, we add a date picker to allow for easy selection of a date without typing it. There are plenty of user controls on the market, and one I appreciate particularly for its ease of use is the JQuery UI DatePicker.

That’s how the Create view looks like:

The Create view.

 

And this is the very simple JavaScript code required for turning the DateOfBirth text input into a date picker with quick selection of month and year (for a date of birth or our users, we’re likely picking a year far back in the past):

$(function () {

    $("input[name='DateOfBirth']").datepicker({

        changeMonth: true,

        changeYear: true,

        showTimepicker: false

    });

});

Turning the Date of Birth text box into a Date Picker with JQuery UI.

 

Each field is validated independently, for being required or not, and for its format. By using unobtrusive JQuery validation, this is done client-side before the form is submitted. The jquery.validate.min.js and jquery.validate.unobtrusive.min.js scripts are added to the shared layout of the view. These scripts are served locally as they are bundled with the application itself for simplicity of distribution, but in the real world, you may want to consider using a CDN for distributing your static JavaScript files.

If you remember, we have also defined the Email address as unique. This constraint cannot be validated client-side as for the other validation types, so we need to have this validation applied on the server-side, which is in the Controller.

Before we present the Controller, though, this is the error message that we’d like to see when entering a duplicate email address. The message is displayed in a summary area of errors that are not necessarily tied to a specific field (although this one is, but its validation requires the investigation of the entire model, as we shall see in a moment), but rather apply to the entire model.

Validation on uniqueness of the email address.

 

The Controller

After presenting Model and View, time now to complete our MVC application by introducing the Controller, namely the UsersController.

By generating this controller with the automatic scaffolding offered by Visual Studio, and in conjunction with the usage of Entity Framework for our repository requirements, the Users controllers implements the following actions:

·         Index: Displays a list of existing users.

·         Details: Displays the details of a specific user.

·         Create: Presents a form for creating a new user.

·         Edit: Presents a form for editing an existing user.

·         Delete: Prompts for confirmation before deleting a user.

In the CRUD acronym (Create Read Update Delete), Create is obviously the C, Index and Details can be considered the R (many and one reading), Edit is the U and Delete for the D.

The Users controller.

 

As anticipated, let’s analyse how the validation of the uniqueness of the email address is performed. I thought about the best way of doing it, and I shortlisted the following two approaches:

1.     Before adding the user entity to the repository, check that another user with the same email address does not exist already.

2.     As we have decorated the Email property with the Index attribute, this is creating a unique index at database level, which means that uniqueness is enforced at database level and then propagated to the application.

I see a couple of issues with the first approach. (a) It requires a second round to the database to check for existing users with the same email address. (b) It is not concurrency-prone, as another parallel process may create a user with the same email address as my own just after my process has checked for uniqueness (and not found anything), and before committing changes into the database. A lock on the table for avoiding concurrent insert is clearly out of consideration.

The second approach creates a dependency between the model and the repository system (Entity Framework), but at this point, as we are still not in the magic world of OWIN, I think this in an acceptable compromise.

I welcome any better and clever ways of doing it J

P.S. There is a third option, which allows to validate the uniqueness of the Email address also client-side. This would require decorating the Email property with the Remote attribute, defined in System.Web.Mvc. This attributes enforces validation of the property by making an Ajax call to a specified controller and action. This forms of validation required the JQuery validation plug-in. In this way, validation of Email can be done without leaving the page. A couple of considerations here: In terms of concurrency, this is even worse than the first approach, as concurrent processes may “steal” the email address during the Ajax roundtrip. This is even more likely to occur than a server-side process competition, considering that it occurs between the client (browser) and the server in disconnected way. Also, it doesn’t free the model from any dependency, as we would tie it to System.Web.Mvc. And in the world of OWIN this is bad…

So unique index it is, and let EF resolve the conflict at database level. All we have to do is to catch the relevant exception and manage it properly. Now, the bad news is that EF does not have a specific exception for unique index violation. It rather throws a generic DbUpdateException. Thus, I modified the Create action to handle this exception when adding the user to the repository and saving changes, as in the code below:

[HttpPost]

[ValidateAntiForgeryToken]

public ActionResult Create([Bind(Include = "Id,FirstName,LastName,Email,DateOfBirth")] User user)

{

    if (ModelState.IsValid)

    {

        try

        {

            db.Users.Add(user);

            db.SaveChanges();

        }

        catch (DbUpdateException ex) when (ex.HResult == -2146233087)

        {

            ModelState.AddModelError(string.Empty, "Email address already used");

            return View(user);

        }

               

        return RedirectToAction("Index");

    }

 

    return View(user);

}

Handling duplicate email exceptions.

 

Inside the catch statement, we add a model error to the model state with an empty key. This indicates that the error refers to the model as a whole and not to a specific property, and therefore will be displayed in the validation summary area within the view, rather than in line with the Email field. But obviously this can be changed as needed.

Hold on, what about that HResult in the when condition of the catch statement? During my debugging session in search for the right exception type to catch, I found out that the way to discern the specific underline error (not a generic update error, but a specific unique index violation) is to either look at the exception message, or the HResult property.

Exception.HResult is a coded numerical value that is assigned to a specific exception. The error code is a unique number that is assigned to represent the exception. Each exception is mapped to a distinct HResult. Apparently, but from my quick search I could not find official documentation about it, -2146233087 is the HResult code for unique index violation. And so I did in handling the DbUpdateException, adding a when condition (this is a new operator in C# 6), to be more specific about the message to display. For brevity, I am not handling any other exception type, although I strongly recommend doing so, obviously.

 

Coded UI Test Project

Time to implement our first Coded UI test in Visual Studio. To start, we need to add a new Coded UI Test Project to the solution.

Coded UI Test Project in Visual Studio.

 

On adding the project to our solution, we are prompted with a decision to make on how we want to create our coded UI tests:

·         By recording actions, editing UI maps or adding assertions: This is the “visual” way of doing it, which relies on the Coded UI Test Builder for recording user experience sessions, and generating all the code for our actions. We will choose this approach.

·         By using an existing action recording and generating the necessary code to perform similar actions as the recording. This method requires us to write a lot more code manually for generating user experience actions in our application. We’ll look into this as a second step, after we get more familiar with the basics of coded UI tests.

How to generate code for Coded UI Tests.

 

As we choose the first option, the Coded UI Test Builder shows up (typically in the lower right corner of the screen). This test builder provides controls for recording actions directly as we experience our application, turning them into C# code that will be executed to introduce automation of our UI tests (hence the name “Coded UI Test”), and adding assertion to specific fields to validate expected results.

Coded UI Test Builder.

 

Action Recording and Assertions

Action and assertions are recorded for each test. Besides the first test added automatically to the project, additional tests can be added by selecting the “Coded UI Test” item from the Add menu for the test project.

Adding a new Coded UI Test to the project.

 

Let’s start recording our first test scenario for testing the UI of the Create action. After adding a Coded UI test to our project, let’s rename it as CodedUITestUsers. This is the code generated. I have already removed the comments from the Initialize and Cleanup methods, that we are going to use later.

[CodedUITest]

public class CodedUITestUsers

{

    public CodedUITestUsers()

    {

    }

 

    [TestMethod]

    public void CodedUITestCreate()

    {

        // To generate code for this test, select "Generate Code for Coded UI Test" from the shortcut menu and select one of the menu items.

    }

 

    // Use TestInitialize to run code before running each test

    [TestInitialize]

    public void MyTestInitialize()

    {

    }

 

    // Use TestCleanup to run code after each test has run

    [TestCleanup]

    public void MyTestCleanup()

    {

    }

 

    public TestContext TestContext { getset … }

}

The initial structure of a Coded UI Test class.

 

As you can notice, each test class is marked with the CodedUITest attribute defined in the Microsoft.VisualStudio.TestTools.UITesting namespace. When the test is executed the method marked with the TestInitialize attribute is invoked first. Use this method to perform initialisation of resources used by the test, or for initial navigation within the application that you do not want to be part of the test itself. Subsequently, all methods marked with the attribute TestMethod are executed. For the purpose, we have our method CodedUITestCreate meant to test the Create action of a user. Finally, use TestCleanup to run code after each test has run.

When recording UI tests, keep in mind this simple rule of thumb: stick to one screen (page) per test. Testing navigation between pages, especially in a browser where speed of page load depends on the quality of the network connection, can be tricky. As test assertions are based on the existence of controls in a page, asserting whether a control has a specific value may happen before the control has been loaded in the page, thus failing the test. I’ll present a workaround to this problem later, but for now let’s stick to the rule of having one test for each screen.

As we have two pages, the Index view and the Create view, we need two test methods. Even better, as the Index view is always loaded first, we can put the recording for opening this page in the initialization method.

To start recording for the MyTestInitialize method:

1.     Close all browser instances (will say more about this later, it is for performance reasons).

2.     Right click on the MyTestInitialize method, and select "Generate Code for Coded UI Test > Use Coded UI Test Builder" from the shortcut menu to indicate that actions are being recorded for this particular test method.

3.     Click the Record button on the Coded UI Test Builder.

4.     Open a new browser window and enter the URL of the MVC application in the address bar.

 

When done with the recording:

5.     On the Coded UI Test Builder, pause recording and generate code; give a name to the recorded method, for example RecordedMethodIndex. This method will be added to the UIMap.

 

The list of steps recorded is visible in the “Recorded Actions” section of the Coded UI Test Builder. As you can see, the builder has recorded exactly what we have just executed, that is only opening a web page at the address indicated.

Recorded actions for the RecordedMethodIndex method.

 

You will notice that the MyTestInitialize method changes to reflect the call to the RecordedMethodIndex in the UIMap.

[TestInitialize]

public void MyTestInitialize()

{

    this.UIMap.RecordedMethodIndex();

}

The recorded method added to the test initialize method.

 

If you were to dig into the RecordedMethodIndex, you will find this implementation:

public void RecordedMethodIndex()

{

    // Go to web page 'http://localhost:3035/' using new browser instance

    this.UIIndexCodedUItestautoWindow.LaunchUrl(new System.Uri(this.RecordedMethodIndexParams.UIIndexCodedUItestautoWindowUrl));

}

The source code of the RecordedMethodIndex method in the UI Map.

 

This method is contained in the UIMap object and it is automatically generated by the Coded UI Test Builder. Do not modify it, as it would be overwritten every time you change the UIMap. There is a different way to change generated code, which we’ll see in a moment.

We will talk about the UIMap in the next section. For now, it is sufficient to know that the test has been recorded. That’s just a sequence of actions, though. We are not verifying anything yet. What’s missing is the actual verification that the steps executed are displaying the intended result. Basically, we need to add an assertion and verify whether the test is successful:

1.     Reopen the Coded UI Test Builder for the MyTestInitialize method.

2.     Click the target icon to locate a control in the UI Map.

Locating the “Create New” link control in the UI Map.

 

3.     Click the Add Assertion button and enter the condition for asserting that this control exists in the page, which is basically our verification to say whether the Index page has been loaded correctly.

Adding an assertion for the “Create New” control.

 

4.     Generate the code for this assertion and name the method as AssertMethodIndexPageLoaded.

 

The method has now changes to add the assertion:

[TestInitialize]

public void MyTestInitialize()

{

    this.UIMap.RecordedMethodIndex();

    this.UIMap.AssertMethodIndexPageLoaded();

}

The assertion method added to the test initialize method.

 

This is a complete test now, with actions and assertions, and it can be fully executed. If you try to run the test, you may experience a failure though.

The test failed to find the “Create New” control.

 

I was expecting this. If you look at the sequence of actions from a code perspective, the RecordedMethodIndex opens the application at the indicated URL, and immediately after, the AssertMethodIndexPageLoaded checks that the “Create New” control exists. This check may happen too soon if the web page has not fully loaded yet.

public void AssertMethodIndexPageLoaded()

{

    ...

    // Verify that the 'InnerText' property of 'Create New' link equals 'Create New'

Assert.AreEqual(this.AssertMethodIndexPageLoadedExpectedValues.UICreateNewHyperlinkInnerText, uICreateNewHyperlink.InnerText, "Page load error");

}

Asserting that the “Create New” control exists.

 

To work around problems of page load, we need to introduce a delay between actions or before conditions are checked. This approach will work also when navigating between pages, allowing enough time for a page to load completely before further actions can be undertaken.

To add a delay, open the UI Map in the project, locate the AssertMethodIndexPageLoaded method in the list of UI Actions, right click and select “Insert Delay Before”.

Adding a delay to a UI Action.

 

A one-second delay is added before the verification of the “Create New” control. The duration of the delay can be modified in the properties of the action.

One-second delay added to the UI Actions.

 

From a code perspective, a Playback.Wait instruction is added before the assertion.

public void AssertMethodIndexPageLoaded()

{

    ...

    // Wait for 1 seconds for user delay between actions; Verify that the 'InnerText' property of 'Create New' link equals 'Create New'

    Playback.Wait(1000);

    Assert.AreEqual(...);

}

Adding the delay before the assertion.

 

If you run this test again, it should now complete successfully.

The test completed successfully.

 

Managing the UI Map

The UI Map is a bit like the control panel of all the actions and assertions recorded with the Coded UI Test Builder. A Coded UI Test project typically has one UI Map only, although more can be added for complex user interfaces. A UI Map consists of:

·         UIMap.uitest: This is an XML file that represents the structure of the coded UI test recording and all its parts. These include the actions and the classes in addition to the methods and properties of those classes.

·         UIMap.Designer.cs: This file contains code that is automatically created by the Coded UI Test Builder when a test is created. This file is re-created every time that a test changes, so this is not a file in which we can add or modify code.

·         UIMap.cs: By default, this file contains a partial UIMap class that has no methods or properties.

 

If you determine that one of your test methods in your coded UI test requires custom code, you must move it into the UIMap.cs file. Otherwise, your code will be overwritten whenever the coded UI test is recompiled.

Do you remember that we added a 1-second delay before the assertion on existence of the “Create New” control, so to make sure that the web page loads completely before the control is checked? Now, what if a second is not enough? This approach based on delay is quite arbitrary. A better way of dealing with control validation on page load is to actually wait until the control is ready. Unfortunately, this kind of wait cannot be added via the UI Map, we need to code it directly. In order to do so, we need to move our assertion method into the UIMap.cs for further customisation. This can be done from the UI Map visualisation itself, by locating the AssertMethodIndexPageLoaded, and selecting “Move code to UIMap.cs” from the context menu. The method is removed from the UIMap.uitest file and no longer is displayed in the UI Actions pane. To edit the method just moved, we need to open the UIMap.cs file from Solution Explorer.

Warning: Once you have moved a method, you can no longer edit it using the Coded UI Test Editor. You must add your custom code and maintain it using the Code Editor.

Moving generated code to UIMap.cs for customisation.

 

Now that the AssertMethodIndexPageLoaded exists in the UIMap.cs file, we can modify it manually with no risk of being overwritten the next time we add a new test to the project. So let’s get rid of the Playback.Wait instruction, and let’s replace it with a more convenient uICreateNewHyperlink.WaitForControlReady, where uICreateNewHyperlink is a local variable identifying the “Create New” HTML hyperlink control. This method will hang the execution of the AssertMethodIndexPageLoaded method until that control is ready. Safe to use if you are absolutely sure that the control will display eventually, but if you are not, you’d better use the overloaded WaitForControlReady method that accepts a timeout period in milliseconds, passed which the execution will carry on with no further wait on the control.

public void AssertMethodIndexPageLoaded()

{

    #region Variable Declarations

    HtmlHyperlink uICreateNewHyperlink = this.UIIndexCodedUItestautoWindow.UIIndexCodedUItestautoDocument.UICreateNewHyperlink;

    #endregion

 

    //Playback.Wait(1000);

    uICreateNewHyperlink.WaitForControlReady();

    Assert.AreEqual(...);

}

Waiting for the “Create New” control to be ready.

 

Time to carry one with an additional UI test. We are inside the Create view, let’s test that all controls work and a new user is correctly created. For doing this, we add a CodedUITestCreate method to the CodedUITestUsers class, and we mark it with the TestMethod attribute. In this way, we are instructing Visual Studio to execute MyTestInitialize first, and then all the methods marked as TestMethod.

As usual, we use the Coded UI Test Builder for recording our actions on the MVC application, now starting from the Create view, and we name this method RecordedMethodCreate in the UI Map. I perform actions on the user interface as a regular user would do. All actions performed on the browser will be recorded. Pause the recording if you are performing actions that may lead to sensitive data being included in the recording.

 

In the UI Map, the RecordedMethodCreate shows a list of UI actions.

UI Actions for the recorded Create method.

 

As you see, the actions in the list reflect exactly the sequence of steps that we have done in the browser. From a code perspective, briefly: All steps are coded either as a Mouse or Keyboard actions. Controls’ properties (in this case specifically the Text property) are assigned a value when I entered something by typing on the keyboard, as in the case of the First Name, Last Name and Email Address,

public void RecordedMethodCreate()

{

    // Click 'Create New' link

    Mouse.Click(uICreateNewHyperlink, new Point(49, 11));

 

    // Type 'Bob' in 'First Name' text box

    uIFirstNameEdit.Text = this.RecordedMethodCreateParams.UIFirstNameEditText;

 

    // Type '{Tab}' in 'First Name' text box

    Keyboard.SendKeys(uIFirstNameEdit, this.RecordedMethodCreateParams.UIFirstNameEditSendKeys, ModifierKeys.None);

 

    ...

 

    // Click 'Create' button

    Mouse.Click(uICreateButton, new Point(56, 27));

}

The source code of the RecordedMethodCreate method.

 

Thinking of the kind of assertions that we can add to this method, I would include a pre-assertion on the URL of the Create view, to make sure that we are opening the correct page when clicking on the “Create New” link in the Index page. Then, a post-assertion to verify that the user has been created correctly after selecting the “Create” button in the Create view.

The AssertMethodCreateViewOpened that I generate with the Coded UI Test Builder asserts that the Uri property of the Window control ends with “/Users/Create”, which is the route of the Create view in the Users controller in the MVC application.

Asserting that the correct Create view is opened.

 

However, the sequence of actions as recorded in the RecordedMethodCreate method do not start from the Create view already, but rather from the Index page. So we need to split our tests into two recorded methods, one for the navigation from the Index to the Create page, performs the assertion, and then the second part for entering values in the page controls. Alternatively, you may want to consider introducing a separate test method. However, as sequence of execution of test methods is not guaranteed, and because each test method should be atomic, that is not dependent on the outcome of another test method, it is not recommended to have separate test methods in this case. Better then, to split the recorded actions into two methods within the UI Map and keep the same test method at project level.

To split a record method, use the UI Map, identify the first action to move to a separate method, and select “Split into a new method” from the context menu:

Splitting a recorded method.

 

The current method will contain the action to click on the “Create New” link, whereas the remaining actions will go under a new method. We’ll call the former method RecordedMethodCreateNew, and the latter RecordedMethodCreate.

The second assertion instead is upon creation of the user. After entering the proper values in the relevant fields in the Create view and saving the entry, the Index page is reloaded and the newly created user displayed in a table structure.

Bob is there…

 

To verify that the user has been saved properly, we can assert that each field in the table row contains the correct information. We also need a recorded action on load of the Index page to allow to obtain a reference to the controls – the table cells – that contain the values to verify. As we have already recorded our RecordedMethodCreate method before, we can add a new action manually without performing a new recording again. Also, there are some actions that cannot be recorded with the Coded UI Test Builder, such as a mouse hover. But we can code a mouse hover action programmatically with the instruction Mouse.Hover. To do so, first we need to move the RecordedMethodCreate method out of the automatically generated UI Map into UIMap.cs as we have seen previously, and then add the following lines of code at the end of the method (the full source code is in the project on CodePlex).

// Hover on email address link when the Index page reloads

uIEmailAddressHyperlink.WaitForControlReady();

Mouse.Hover(uIEmailAddressHyperlink);

Adding a mouse hover action programmatically.

 

The assertion will then be added via the Coded UI Test Builder for the fields in the table to match the information entered in the Create form. The final test consists of two steps then:

·         Open the Create view and assert that the correct page URL.

·         Create the user and assert the information is correct.

[TestMethod]

public void CodedUITestCreate()

{

    this.UIMap.RecordedMethodCreateNew();

    this.UIMap.AssertMethodCreateViewOpened();

 

    this.UIMap.RecordedMethodCreate();

    this.UIMap.AssertMethodUserCreated();

}

The Coded UI test for the Create view.

 

As you can see, the UI Map is our centre of control of all coded operations, actions and assertions, with freedom of usage via a more visual oriented approach with the Coded UI Test Builder, or complete control of the generated source code programmatically.

 

Data-Driven Coded UI tests

Our Coded UI test is now ready to be automated and executed repeatedly. We may want to test for different data and conditions, test validation, etc. The first thing that we notice, however, is that as soon as we run the test again… it will fail!

Ops, the test failed again…

 

What happened? Easy said: The test is hard coded to create our user Bob Smith with the same email address over and over. This is a problem, because the email address is a unique field and cannot accept duplicate values. Hence, the test fails because it cannot insert a duplicate user. However, to be more specific, our UI test is not failing for this specific reason. From a UI perspective, the assertion that is failing is what we coded previously, that is the table field with the email address of the user cannot be found. This is happening because the Index page is not loaded after creating the user, as there is a validation message displayed within the Create page itself. This is visible if we open the output attachment of the test exception. In the Test Explorer window, next to the failed test, there is a description of the exception encountered, and just beneath it, the Output hyperlink. This brings to the attachment page, where we can see a screenshot of the page where the test failed. As you can find out, that’s exactly the Create page with the error message about the email address validation.

Failed test output.

 

That’s a great help for debugging and filing this error as a bug, providing visual evidence. But we can do better J. There is a setting that we can enable in our recorded test for collecting additional output information, not only when a test fails, but for each step before too. This is extremely convenient to understand the sequence of actions that led to the test failure. To enable collecting snapshots for each actions, add this line at the beginning of the RecordedMethodCreate method:

Playback.PlaybackSettings.LoggerOverrideState = HtmlLoggerState.AllActionSnapshot;

 

After running the test again, and getting the expected exception, the output attachment is now an HTML page that contains much more detailed information about each action of the coded UI test, along with execution duration.

Detailed all-action-snapshot test output report.

 

Now that we have detailed information about the root cause of the test failure, we can remedy it. This is the time to introduce Data-Driven Coded UI Tests. To test different conditions, we can run our test multiple times with different parameter values. Data-driven coded UI tests are a convenient way to do this. All we have to do is define parameter values in a data source, and each row in the data source is an iteration of the coded UI test. The overall result of the test will be based on the outcome for all the iterations. For example, if one test iteration fails, the overall test result is failure.

Let’s add a data set: This is simply a text file to add to the project, let’s call it Users.csv. This file contains the first row with headings named after the User model, and then a row for each new user we want to test creation.

FirstName,LastName,Email,DateOfBirth

Al,Pacino,alpacino@godfather.movie,04/25/1940

John,Travolta,jtravolta@grease.movie,02/18/1954

Sandra,Bullock,sbull@gravity.movie,07/26/1964

The Users.csv file to use as data source.

 

It is important to save the .csv file using the correct encoding. On the File menu in Visual Studio, open Advanced Save Options and choose Unicode (UTF-8 without signature)Codepage 65001 as the encoding.

Correct encoding of the data source file.

 

Additionally, the .csv file, must be copied to the output directory, or the test cannot run. Use the file’s Properties window to copy it.

Copy the data source file to the output directory.

 

To bind the data source to the test method, we have to add a DataSource and a DeploymentItem attribute to the CodedUITestCreate method. The data source is now available to use in this test method. Other data source types such as XML, SQL Express and Excel can be used, besides CSV, by configuring the proper data source attributes.

[TestMethod]

[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "|DataDirectory|\\Users.csv", "Users#csv", DataAccessMethod.Sequential) , DeploymentItem("Users.csv")]

public void CodedUITestCreate()

{

Binding the data source to the test method.

 

Next, we’ll have to configure the test to use the values in the data source file. Values from the data source can be obtained with a reference to TestContext.DataRow. The data source values override the constants assigned to UI Map controls in the Create page, by using the recorded parameters. These are accessible directly from the UI Map in the RecordedMethodCreateParams object. Then we need to reference the control’s SearchProperties for validation, in the Index page. To figure out which search properties to code the data to, we need to have a look at the UI Control Map. Then, in the CodedUITestCreate test method, override the specific control’s search property with the pertinent value from the data source.

[TestMethod]

[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "|DataDirectory|\\Users.csv", "Users#csv", DataAccessMethod.Sequential), DeploymentItem("Users.csv")]

public void CodedUITestCreate()

{

    this.UIMap.RecordedMethodCreateNew();

    this.UIMap.AssertMethodCreateViewOpened();

 

    var uiCreate = this.UIMap.UIIndexCodedUItestautoWindow.UICreateCodedUItestautDocument;

    this.UIMap.RecordedMethodCreateParams.UIFirstNameEditText = TestContext.DataRow["FirstName"].ToString();

    this.UIMap.RecordedMethodCreateParams.UILastNameEditText = TestContext.DataRow["LastName"].ToString();

    this.UIMap.RecordedMethodCreateParams.UIEmailEditText = TestContext.DataRow["Email"].ToString();

    this.UIMap.RecordedMethodCreateParams.UIDateOfBirthEditText = TestContext.DataRow["DateOfBirth"].ToString();

    this.UIMap.RecordedMethodCreate();

 

    var uiIndex = this.UIMap.UIIndexCodedUItestautoWindow.UIIndexCodedUItestautoDocument;

    uiIndex.UIItemTable.UIBobCell.SearchProperties[HtmlHyperlink.PropertyNames.InnerText] = TestContext.DataRow["FirstName"].ToString();

    uiIndex.UIItemTable.UIBobCell.SearchProperties[HtmlHyperlink.PropertyNames.InnerText] = TestContext.DataRow["LastName"].ToString();

    uiIndex.UIItemTable.UIBobCell.SearchProperties[HtmlHyperlink.PropertyNames.InnerText] = TestContext.DataRow["Email"].ToString();

    uiIndex.UIBobsmithsomedomaincoHyperlink.SearchProperties[HtmlHyperlink.PropertyNames.InnerText] = TestContext.DataRow["DateOfBirth"].ToString();

    this.UIMap.AssertMethodUserCreated();

}

The final CodedUITestCreate test method with bound data source.

 

For the three records in the Users.csv data source, three iterations of the test will execute; for each iteration, this test will change the Inner Text value for the search property of the identified controls.

 

About Performance and the Need for Multiple UI Maps

Before closing, the last consideration is on improving performance of Coded UI tests and scaling its automation to large applications. If you have noticed when executing the test, filling the form and navigating from one page to another one doesn’t seem to be very quick. You’d say, a person can be even quicker than that! How is this helping me saving time? One of the reason why coded UI tests may perform slowly depends on the way controls are identified in a page, and their properties modified. Each control has some attributes, like Id, Name, Inner Text, that are used for identifying it within a page. These attributes are called Search Properties. A page itself has its own set of Search Properties used for identifying it within a window. Lastly, a window, that is a browser window for web-based applications, but can be a Windows form for Windows desktop applications, is also identified at runtime based on its Search Properties. The more windows, pages and controls are in the UI Map of a Coded UI Test project, the longer it takes for the test engine to identify them, match the property with the recorded information, and assign a new value, for example taken from a data source. So, keep your browser closed when running an automated Coded UI test. This will help the test engine find the searched window more quickly.

To scale to large applications with a very rich user interface, hundreds of screens and controls, it is convenient also to consider using multiple UI Maps. The generated code for the UI Map contains a class for each object that the test interacts with. For each generated method, a companion class for method parameters is generated specifically for that method. If there are a large number of objects, pages, and forms and controls in your application, the UI Map can grow very large. Also, if several people are working on tests, the application becomes unwieldy with a single large UI Map file.

Using multiple UI Map files can provide the following benefits:

·         Each map can be associated with a logical subset of the application. This makes changes easier to manage.

·         Each tester can work on a section of the application and check in their code without interfering with other testers working on other sections of the application.

·         Additions to the application UI can be scaled incrementally with minimal effect on tests for other parts of the UI.

 

In Conclusion

A few best practices for preparing satisfactory Coded UI Tests:

·         Use the Coded UI Test Builder whenever possible to record actions and add assertions. refuge to writing code only when strictly required, e.g. for adding actions that cannot be recorded.

·         Do not modify the UIMap.designer.cs, simple as that! It will be overwritten every time you add a new test. Instead, move your methods to the UIMaps.cs file if you need to add custom code.

·         Create a new test method for each new page to minimise locks on waiting for page loads, otherwise introduce delays or wait conditions on controls.

·         Close any other browser window! Performance of execution of Coded UI tests is severely impacted by the presence of multiple instances of the browser.

 

When to use Coded UI Tests

When Not to use Coded UI Tests

Repetitive UI tasks

Time-consuming data entry

Complex regression testing

You don’t want to burn the midnight oil…

For Unit and Integration testing

Applications with no UI

Backend Integrations

Web Services

As replacement for Load Tests

 

A lot more can be written about Coded UI test automation. For additional information, please refer to this article on MSDN: Use UI Automation To Test Your Code.

 


  Comments

 

 Source Code

Project Name: CodedUIMvc


 Related Content
A flexible Default Value for your DateTime properties
When creating an MVC application with Entity Framework, it is possible to set default values for most properties in a model using the DefaultValue attribute. However, no much flexibility is offered for a DateTime property. This article presents a custom validation attribute for DateTime types that accepts different formats for defining the default value of the property.
Adding a Secured Geo-located Audit Trail
How I built a social sharing component for my own web site and added a secured geo-located audit trail. Step by step, let’s analyse technologies and source code for developing this component.
Adding Social Sharing to a Web Site
How I built a social sharing component for my own web site and added a secured geo-located audit trail. Step by step, let’s analyse technologies and source code for developing this component.
Best practices for mobile form design in SharePoint
Build effective SharePoint forms with Nintex that are accessible anywhere, at any time, and on any device. You built the workflows, you built the forms, now make them mobile.
Bring your “A” game to ESPC16
With just over 3 weeks to go to Europe's largest gathering of SharePoint & Office 365 professionals, take a look at these tips that will help you get the most out of ESPC16…
Building an MVC application for SharePoint
Learn how to write code to perform basic operations with the SharePoint 2013 .NET Framework client-side object model (CSOM), and build an ASP.NET MVC application that retrieves information from a SharePoint server.
CIO vs CTO
What are the synergies and differences of the roles of a Chief Information Officer and a Chief Technology Officer? An open conversation about two roles with one mission…
Converting GIS spatial coordinates
Different formats and standards exist for describing geographical coordinates in GIS systems and applications. This article explains how to convert between the most used formats, presenting a working library written in C#.
Creating mobile accessible forms in SharePoint
With the release of the Nintex Mobile apps, SharePoint users can now optimise their experience across popular mobile devices and platforms.
Define your Performance Testing strategy with Visual Studio
Performance Testing is an essential part of software testing, with the specific goal of determining how a system performs in terms of responsiveness and stability under a particular workload. In this series of posts we’ll define and execute a good strategy for testing performance of an application using Visual Studio.
Disserting about colliding GUIDs and the Big Bang Theory
Can you generate two identical GUIDs? Would the world end if two GUIDs collide? How long does it take to generate a duplicate GUID and would we be still here when the result is found?
GIS Location Services in Dynamics CRM
A design paper about implementing GIS-based services for a local Council in Dynamics CRM, structuring address data, and delivering location services in the form of WebAPI endpoints via an Enterprise Service Bus.
Group or Team?
All teams are groups but not all groups are teams. What defines a group and what a team? When do we need one over the other one?
How to Give Constructive Feedback
Learning to give and receive constructive feedback is an essential part of learning, growing, improving and achieving our goals.
Mirroring an iPad on your laptop
Have you ever wanted to see your iPhone or iPad on a larger screen? Play games, watch movies, demo apps or present to your computer from your iPhone or iPad. Reflector mirrors iOS devices on big screens wirelessly using iOS built-in AirPlay mirroring.
Mobilize your SharePoint workflows
Build workflow applications in SharePoint that can be accessed on mobile devices using the Nintex solution for business process mobilization.
Natural String Sorting
Have you ever desired to have in your code a way to order a sequence of strings in the same way as Windows does for files whose name contains a mix of letters and numbers? Natural string sorting is not natively supported in .NET but can be easily implemented by specialising a string comparer and adding a few extensions to the enumerable string collection.
Sales Effectiveness in Dynamics CRM with Azure IoT and Machine Learning - Part 1
How can an organisation optimise its sales channels and product targeting by building a 365-degree view of its customers in Dynamics CRM? The answer, and topic of this article, is with the help of Azure IoT and Machine Learning services!
Scaling Applications with Azure Redis and Machine Learning - Part 1
This article presents design best practices and code examples for implementing the Azure Redis Cache and tuning the performance of ASP.NET MVC applications, optimising cache hit ratio and reducing “miss rate” with smart algorithms processed by Machine Learning.
Software Development Management in 11 steps
What it takes to be a great Software Development Manager? I have been building software for the last 15 years and have collected a few stories and experiences to share. Some I have used as questions when interviewing candidates. In 11 points, this is my story to date.
SOLID SharePoint apps with MVC
Practical code examples of ASP.NET MVC applications that connect to a SharePoint Server and comply with the SOLID principles.
The art of outsourcing projects
Outsourcing may look financially attractive, but working with companies in far-off lands introduces challenges that, if not considered properly, can drive any project to failure. Let’s explore some common pitfalls when working with offshore partners and a common-sense approach to work around them.
The value of hashtags
Customers expect a modern approach to advertising. Digital advertising can leverage evolving technology to provide just-in-time, just-at-the-right-place promotions.
We don't need no Yoda's syntax
There is an urban myth in the programmers’ community that the so called “Yoda’s syntax” performs better when checking an object for nullity. Let's demystify it...