Archive

Posts Tagged ‘C#’

Coded UI: Run same test against multi regional application

18/01/2013 2 comments

As of writing this blog I had difficulty in finding examples of how other Coded UI developers managed to re-use their same tests against other language version of their application under test. In my case the application under test wasn’t internationalised but had a similar problem where the application was hosted for multiple clients which the data, menu items and labels on the screen could have different values but the underlying functionality was the same. I did not want to write a test for each clients copy of the application and figured it shouldn’t be to difficult to re-use one test across all clients. I was surprised not to find such examples but worked out my own solution. This blog will attempt to explain the approach I took.

 

Gotchas:

This approach requires separate test data drive files for each language tested. For this reason you will not be able to bind your tests to Microsoft Test Manager (MTM) test case parameters. This post will use CSV files instead to data drive its tests.

 

Requirements:

  • You cannot data drive your tests using MTM test case parameters.
  • You will require separate test data files for each language you want to test.
  • I will demonstrate how to run the same tests for multiple languages To do this will require multiple Test Settings created within MTM or visual studio.
  • If you use the code in the getting started section below you will need IIS.
  • You must have either Visual Studio Premium or Ultimate to be able to create Coded UI Tests.
  • Please note that I am using Visual Studio 2012, this is not a requirement but might make it easier to follow along with this blog.
  • I assume you know the basics of Coded UI and data driving your tests.

Getting started: (Skip if you have something to test)

First thing we need is something to test. This section we will create a multi regional web site to test against. You can skip reading this section if you already have a multi regional system or something of similar concept to test against. This example will not follow best practice for creating multi regional applications like using satellite assemblies, instead I will simply hard code language fields based on a querystring passed into the web site. Lets keep it nice and simple and just create a Hello World Asp.Net MVC 4 type application. Please note I am not going to explain much about how the code works as that is not the focus of this blog.

  • open up visual studio
  • select a new project and create an ASP.NET MVC 3 or 4 web application
  • Name the project “HelloWorld” and set the solution name to “HelloWorldSolutions”.

newproject

  • Select Empty template

emptyTemplate

  • Under the Controllers folder for the project in solution explorer, create a new Controller, call it “HomeController” and select Empty MVC Controller under templates.

CreateController

  • Paste the following code into it:
using System.Web.Mvc;
using HelloWorld.Models;
 
namespace HelloWorld.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return Index("en");
        }
 
        [HttpGet]
        public ActionResult Index(string region)
        {
            SetLabelsToLanguage(region);
 
            return View(new Person());
        }
 
        [HttpPost]
        public ActionResult Index(Person person, string region)
        {
            SetLabelsToLanguage(region);
 
            return View(person);
        }
 
        private void SetLabelsToLanguage(string region)
        {
            switch (region)
            {
                case "fr":
                    ViewBag.HelloLabel = "Bonjour";
                    ViewBag.EnterNameLabel = "Veuillez entrer votre Nom";
                    ViewBag.SubmitLabel = "Soumettre";
                    break;
                default:
                    ViewBag.HelloLabel = "Hello";
                    ViewBag.EnterNameLabel = "Please enter your Name";
                    ViewBag.SubmitLabel = "Submit";
                    break;
            }
        }
    }
}
  • This code makes use of an MVC Model class called Person. Create a class called Person under the Models folder in the project and paste the following code into it:
namespace HelloWorld.Models
{
    public class Person
    {
        public string Name { get; set; }
    }
}
  • Compile the code at this point. Code needs to be compiled in order to proceed with the next step.
  • The HomeController needs a view page. In the HomController class code, right mouse click on any of the words in the code named “View” and select “Add View”.

AddView

  • In the dialog box that shows, name the view “Index”, use the Razor engine, check Create a strongly typed view and select Person as the model (this is why we had to previously compile). Then click Add.

AddViewDialog

  • Paste the following code into it.
@model HelloWorld.Models.Person
 
@{
    ViewBag.Title = "Hello World";
}
 
@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
 
    <fieldset>
        <legend>@ViewBag.HelloLabel @Model.Name</legend>
 
        <div class="editor-label">
            @ViewBag.EnterNameLabel
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>
 
        <p>
            <input type="submit" value="@ViewBag.SubmitLabel" />
        </p>
    </fieldset>
}
  • You need to host the site on IIS to make it easier to test the site using Test Manager. Right mouse click on the project in solution explorer and select properties. On the Web tab, select the radio button “Use Local IIS Web server”. Make sure the “Use IIS Express” is not checked. specify the url where you want to host it and click the Create Virtual Directory button.

WebProperties

  • Save all, rebuild and run the project. When the web site loads, enter your name and hit the submit button. Notice the name is displayed in the title after hitting the submit button. Should look like the following:

Site

Another great Hello World application. You can also run the site in French by adding the querystring parameter at the end of the url: ?region=fr

SiteFrench

Please mind my French but I used Google translator as I don’t really know French.

Creating the Test

So now that we have something to test lets create the Coded UI test.

Tip: Its very important to start creating your tests with multilingual in mind. It can be virtually impossible to change your tests to deal with this later on once you have a few hundred tests or more written. It would normally take to much work to make the changes.

  • Add a new project to the solution. Select the Test Project Filter Tab and then select a Coded UI Test Project. Name the project “HelloWorldUITests”.

HelloWorldUITests

  • When the Generate code for Coded UI Test displays, just click cancel.
  • Compile and run without debug the web site project again. Leave the web site open and go back to Visual Studio.
  • Right click on the HelloWorldUITests project and Add a new Item. Select the Test item filter type tab and then select Coded UI Test Map. Name the item “HelloWorldPageUIMap.uitest”.

HelloWorldPageUIMap

  • After clicking the Add button it should have started the Coded UI Test Builder. We are not going to use the record button but instead to keep things neater we are simply going to use the cross hair button to map items on the page. Drag the Cross hair over the Text box input on the Hello World site

CrossHairTextBox

  • Expand the form that pops up and click on the Add icon to add the Text box to the UI Map

TextBoxMap

  • Repeat the process of mapping items on the web page until you have mapped the Submit button and the Hello Title as well. The Hello title can be a little difficult to get the cross hair to highlight, use Coded UI Test builder’s control locator to navigate parent and children controls until you locate the control with the TagName = LEGEND

MapBuild

  • Click on the Generate Code button on the code builder, then click on the Generate button on the dialog that pops up

GenerateCode

  • Close the Code Builder, this should bring you back into Visual Studio

CloseBuilder

  • Make sure all the controls mapped have the correct name. In solution explorer double click on the “HelloWorldPageUIMap.uitest” file, this should bring up a map editor. Go through and name each object in the editor the same as the image bellow:

MapNames

  • Right mouse click on the “UISubmitButton”. Click on the triple ellipse button “…” on the “Search properties”. Delete the “DisplayText” entry. This will cause issues in the next section of this blog if not done

SearchProperties

  • Now open the file CodedUITest1.cs, in there delete the Test method named “CodedUITestMethod1” and then paste the following test method code into the class
        [TestMethod]
        public void HelloWorldTest()
        {
            Uri url = new Uri("http://localhost/HelloWorld/&quot;);
            string name = "Centaurian";
            string expectedHello"color: #A31515; ">"Hello Centaurian";
 
            using (var browser = BrowserWindow.Launch(url))
            {
                var map = new HelloWorldPageUIMap();
                map.UIHelloWorldWindow.CopyFrom(browser);
 
                map.UIHelloWorldWindow.UIHelloWorldDocument.UINameEdit.
 
                Mouse.Click(map.UIHelloWorldWindow.UIHelloWorldDocument.UISubmitButton);
 
                Assert.AreEqual(expectedHello
            }
        }
  • In the code you pasted from above, replace the url to point to where you are hosting the site.
  • Now compile and run the test. It should launch the site, enter the name, hit the submit button and then assert that the title says “Hello Centaurian”.

Great, we now have a Coded UI test for our site.

Time for the Magic

You might have noticed that so far we are only testing the English version of the site. Here I will explain how to test both the English and the French version while re-using the same test method written in the previous section.

To do this requires the need to data drive the test and then pass a data file to the test containing  a language specific set of data.

  • First change the Test Method code to the following:
        [DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "TestData\\fr\\HelloWorldTest.csv", "HelloWorldTest#csv", DataAccessMethod.Sequential), DeploymentItem("TestData\\fr\\HelloWorldTest.csv", "TestData\\fr"), TestMethod]
        public void HelloWorldTest()
        {
            Uri url = new Uri(TestContext.DataRow["Url"].ToString());
            string name = TestContext.DataRow["Name"].ToString();
            string expectedHelloText = TestContext.DataRow["ExpectedHelloText"].ToString();
 
            using (var browser = BrowserWindow.Launch(url))
            {
                var map = new HelloWorldPageUIMap();
                map.UIHelloWorldWindow.CopyFrom(browser);
 
                map.UIHelloWorldWindow.UIHelloWorldDocument.UINameEdit.Text = name;
 
                Mouse.Click(map.UIHelloWorldWindow.UIHelloWorldDocument.UISubmitButton);
 
                Assert.AreEqual(expectedHelloText, map.UIHelloWorldWindow.UIHelloWorldDocument.UIItemCustom.InnerText);
            }
        }
  • You will notice that the code above now makes use of a file named  "HelloWorldTest.csv" to data drive the url, name and expectedHelloText variables. It will look for this file within the directory “\TestData\fr\”.
  • Create a folder under the “HelloWorldUITests” project in solution explorer and name the folder “TestData”. Create another folder under the TestData folder and name it “fr”.
  • Add a New Item under the “fr” folder in solution explorer, select the “Text File” item type and name it “HelloWorldUITest.csv”

HelloWorldTestcsv

  • Open the “HeloWorldTest.csv” file and paste the following text into it. Be careful that there are no spaces at the end of each line and no extra rows.
x,Url,Name,ExpectedHelloText
x,http://localhost/HelloWorld/?region=fr,Centaurian,Bonjour Centaurian

The column of data above named “x” is because sometimes there is a bug where the first columns name contains strange characters when running the code. This causes the tests to fail because it won’t find the column named Url. The ‘x’ column allows us to ignore this bug.

  • Replace the Url value in the “HeloWorldTest.csv” file to point to where you are hosting your site. Make sure to keep the querystring “?region=fr” at the end of the url.
  • Right mouse click on the “HeloWorldTest.csv” file and select properties. In properties panel change the value for property “Copy to output directory” to “Copy always”.
  • Save, compile and run the test again at this point. It should load and test the French version of the site this time.

BonjourSite

  • Now we need to create an English version of the data file. Create another folder under the TestData folder and name it “en”. Add a New Item under the “en” folder in solution explorer, select the “Text File” item type and name it “HelloWorldUITest.csv”. Paste the following lines of text into it again making sure there are no extra spaces at the end of each line and no extra lines. Replace the url making sure to keep the querystring “region=en”. Set the property “Copy to output directory” on the file to “Copy always”.
x,Url,Name,ExpectedHelloText
x,http://localhost/HelloWorld/?region=en,Centaurian,Hello Centaurian
  • To quickly test the English version of the data file, change the data drive attributes above the test method to the following code, then compile and run the test again.
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "TestData\\en\\HelloWorldTest.csv", "HelloWorldTest#csv", DataAccessMethod.Sequential), DeploymentItem("TestData\\en\\HelloWorldTest.csv", "TestData\\en"), TestMethod]
public void HelloWorldTest()

At this point you are able to switch between running the English or French version by adjusting the data drive attribute values above. Not bad but now I want to be able to switch by simply selecting the Test Run Configuration to run under in Visual Studio or Test Manager.

Both Visual Studio and Test Manager allows you to specify a DOS batch file to be called before and after a suite of tests is run (setup & cleanup scripts). We can use the setup script to copy either the French version or English version of the CSV file into a new folder where our test will point to. Then all we need is a French and English Test Run Configuration which will have their own Batch file copying their respective csv file. We can then simply toggle between configurations.

Info: Behind the scenes Visual Studio or Test Manager actually merges the contents of this batch file into another batch file it generates called “qtsetup.bat”. It will only creates this “qtsetup.bat” file if you have specified a setup or cleanup script. When running tests using Test Manager you can find the “qtsetup.bat” file under
“%userprofile%\AppData\Local\VSEQT\QTAgent\{test guid}”.
When running tests using Visual Studio the file can be found under the solutions TestResults\{test run} folder. Note, Visual Studio 2012 deletes the “qtsetup.bat” file after the test run, you will need to run the test in debug mode and set a breakpoint to see the file before it gets deleted. 

If you looked inside the “qtsetup.bat” file you will notice that it sets a few interesting parameters which can be used to determine which controller and agent is running the test, where files are deployed to and tested from and where test results or stored.

  • Under the “HelloWorldUITests” project in solution explorer add a folder called “Batch”. Under this folder add a text file and name it “French.bat”. Paste the following into it:
copy .\TestData\fr\*.* .\TestData

 

  • Now create an English.bat under the Batch folder and paste the following into it:
copy .\TestData\en\*.* .\TestData

 

  • Create a network share of the Batch folder. Make sure the windows account which you run visual studio under and also the account the Test Manager agent runs under has read permissions on this share.
  • Right click on the solution and add a new item to the solution, select Test Settings and name it “French.testsettings”, do the same again and create one called “English.testsettings”.
    CreateTestSettingsFile

 

  • In the Test Settings window that displays after creating the Test Settings file. Select the Deployment tab, then make sure the “Enable deployment” check box is checked. This will ensure the TestData files are still deployed with the test run.

EnableDeployment

 

  • Next, select the “Setup and Cleanup scripts” tab, Set the “Setup Script” to point to the share created for the Batch folder and specify the French.bat file for the “French.testsettings” and English.bat for the “English.testsettings”.

SetupAndCleanupScripts

  • Apply and close the Test Settings window.  Now replace the Data drive attribute above the test method with the following:
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "TestData\\HelloWorldTest.csv", "HelloWorldTest#csv", DataAccessMethod.Sequential), DeploymentItem("TestData", "TestData"), TestMethod]

 

  • Compile the code and select either French or English test settings on the visual studio toolbar and run the test. Note, you may need to first use the “Select Test Settings File” on the menu before they appear on the menu list like mine below.
    SelectTestSetting

Viola! Now you can test either the French or English version simply by switching between the French or English test setting file on the visual studio top menu.

Tip: If the test fails complaining that it can’t find the “HelloWorldUITest.csv” file. You might need to navigate to the bin folder of the test project and delete everything under it and then compile again. This is because the compiler does not always deploy the csv files again into the bin folder if they already exist even though we set the property on the file to copy always. This starts to happen once you switch to using a test settings file.

I assume you know how to link your Coded UI test to MTM. MTM also has a test setting which can be selected using the Run with options. MTM’s test settings also has the ability to specify a setup & cleanup script.

Advertisements