A guide to web coding in TestComplete. I will be using the JScript language.

Getting Started:

Start by going to the TestComplete website and register for a free trial. Once you have registered you should be emailed a key and a link to download the test complete software (you might want to grab the executable of a college to save some bandwidth).

I will only be using the Web component, so that’s all I will install:

Once you have installed TestComplete (Web), you should probably ‘prepare‘ the browser you want to use and I would recommend  running through the web tutorial provided by SmartBear before reading further.
Note: If you run into problems using all the browsers, you might need to enable the TestComplete plug-in. For example Chrome had the extension disabled by default meaning the tests would not work when executed.

Creating Scripts:

Once you have run through the tutorial I think it is worth transitioning to writing your tests as scripts. The language I have chosen in JScript (you choose this when you create a project). I chose this language as it is the most similar to C++ – “JScript […] is more similar to C++ and C# than the C++Script and C#Script syntax is”. You can read more here.

The first thing I did was right-click on the KeywordTest and select “Convert to Script”. This gives us a good idea on what to expect. I use this feature quite a lot to quickly get the syntax for a command.

I’m going to modify the automatically generated code. Lets start by allowing us to easily change browsers. At the top of the script I will add:

    //var BROWSER = btChrome
    //var BROWSER = btIExplorer
    var BROWSER = btFirefox

I will comment out the first two so that I am using Firefox. In the code you can now replace the browser reference to BROWSER:

span { font-family: ‘Courier New’; font-size: 10pt; color: #000000; } .sc0 { } .sc2 { color: #008000; } .sc10 { font-weight: bold; color: #000080; } .sc11 { }

function Example(){ Browsers.Item(BROWSER).Run(); //Launches the specified browser.}

I also moved the URL as a variable:

span { font-family: ‘Courier New’; font-size: 10pt; color: #000000; } .sc0 { } .sc2 { color: #008000; } .sc10 { font-weight: bold; color: #000080; } .sc11 { }

function Example(){ Browsers.Item(BROWSER).Run(); //Launches the specified browser. Browsers.CurrentBrowser.Navigate(URL); //Opens the specified URL in a running instance of the specified browser. }

The next thing I wanted to do arose from the fact I didn’t log the user out at the end of the session. To do this I searched the page body for the string “Logout” and if present called a Logout() function:

span { font-family: ‘Courier New’; font-size: 10pt; color: #000000; } .sc0 { } .sc2 { color: #008000; } .sc5 { font-weight: bold; color: #0000FF; } .sc6 { color: #808080; } .sc10 { font-weight: bold; color: #000080; } .sc11 { }

function Example(){ Browsers.Item(BROWSER).Run(); //Launches the specified browser. Browsers.CurrentBrowser.Navigate(URL); //Opens the specified URL in a running instance of the specified browser. // Check if logged-in an log-out if necessary var str = “Logout”; var browser = Sys.Browser(“*”); var page = browser.Page(“*”); obj = page.NativeWebObject.Find(“contentText”, str, “A”); if (obj.Exists){ Logout(); } } function Logout(){ Aliases.browser.pageWebOrders2.formAspnetform.linkLogout.Click();}

Note that if you want to call a function in a different script file, you need to add a reference at the top of the calling script (somthing like a #include):

span { font-family: ‘Courier New’; font-size: 10pt; color: #000000; } .sc2 { color: #008000; }

//USEUNIT Name_of_Script

If you run the script now, it should log the user out if they did not do so in the previous session. To tidy this up, make the code more reusable and to introduce function paramaters, lets create a function to search for text within the body of a page:

function Example(){ Browsers.Item(BROWSER).Run(); //Launches the specified browser. Browsers.CurrentBrowser.Navigate(URL); //Opens the specified URL in a running instance of the specified browser. /////////////////////////////////////////////////////////////////////// // Check if logged-in an logout if nessacary if (SearchBody(“Logout”)){ Logout(); } function Logout(){ Aliases.browser.pageWebOrders2.formAspnetform.linkLogout.Click();} function SearchBody(str){ var browser = Sys.Browser(“*”); var page = browser.Page(“*”); obj = page.NativeWebObject.Find(“contentText”, str, “A”); if (obj.Exists) return 1; else return 0; }

That’s better I think. The next code you should have enters in the username and password. It should look something like the following:

//Clicks the Login Field Aliases.browser.pageWebOrdersLogin.formAspnetform.panelLogin.textboxMaincontentUsername.Click(); //Sets the username as ‘Tester’ Aliases.browser.pageWebOrdersLogin.formAspnetform.panelLogin.textboxMaincontentUsername.SetText(“Tester”); //[Tab] to next field Aliases.browser.pageWebOrdersLogin.formAspnetform.panelLogin.textboxMaincontentUsername.Keys(“[Tab]”); //Sets ‘test’ as the password Aliases.browser.pageWebOrdersLogin.formAspnetform.panelLogin.passwordboxMaincontentPassword.SetText(“test”); //[Enter] to login Aliases.browser.pageWebOrdersLogin.formAspnetform.panelLogin.passwordboxMaincontentPassword.Keys(“[Enter]”);

Instead of hardcoding in the username and password lets read in these variables from a file. Maybe we want to read a couple of sets of user credentials and repeat a process on both the users. Create a spreadsheet and set it up as follows (save it as a .xls – or modify the below code):

Username Password
Wrong Test
Tester Test

The name of the field should appear across the top row and the sets of data listed below. You can then read in this information using the following script (available from here – read the documentation for other options):

var RecNo; // Posts data to the log (helper routine) function ProcessData(){ var Fldr, i; Fldr = Log.CreateFolder(“Record: “ + aqConvert.VarToStr(RecNo)); Log.PushLogFolder(Fldr); for(i = 0; i < DDT.CurrentDriver.ColumnCount; i++) Log.Message(DDT.CurrentDriver.ColumnName(i) + “: “ + aqConvert.VarToStr(DDT.CurrentDriver.Value(i))); Log.PopLogFolder(); RecNo = RecNo + 1;} // Creates the driver (main routine) function TestDriver(){ var Driver; // Creates the driver // If you connect to an Excel 2007 sheet, use the following method call: // Driver = DDT.ExcelDriver(“C:\\MyFile.xlsx”, “Sheet1”, true); Driver = DDT.ExcelDriver(“C:\\Users\\username\\Desktop\\MyFile.xls”, “Sheet1”); // Iterates through records RecNo = 0; while (! Driver.EOF() ) { ProcessData(); // Processes data Driver.Next(); // Goes to the next record } // Closes the driver DDT.CloseDriver(Driver.Name);}

The file should execute without any problem and you should have two records:

I put this code at the end of my original file. At the top I created a main() function which calls TestDriver() sending it the location of the excel file. We now need to create our own ProcessData() function to store the variables in a way we can later use. However, before doing this we need to have a look at how to create classes and arrays of objects.

Classes and Object Arrays:

I find the way TestComplete handles this quite strange and it took me quite some time to get my head around it. Start by creating a new script. You then need to right click on the ‘Advanced’ folder, select ‘Add -> New Item…”:

Then select ODT and press ok.

TestComplete uses this to keep track of both classes, objects and groups of objects. You can create a Class and Object using the following code (see here and here):

function test(){

// Create Class TestClassObj = ODT.Classes.Declare(“TestClass”); TestClassObj.AddProperty(“username”, “”); TestClassObj.AddProperty(“password”, “”); // Create an Object of TestClass TestObj = ODT.Classes.New(“TestClass”); // Set the properties of the object TestObj.username = “felix”; TestObj.password = “mypassword”;
}

Add a breakpoint on the last brace so you can inspect the variables (in the watch window). If you run the script again it will fail because the class can’t be created if it already exists. To fix this add the following code to the top of the script:

//Clean Up ODT.Data.Clear(); ODT.Classes.Clear();

Whilst this is all well and good, to make the script more useful we really want to be able to loop through an array of objects. To create an array we first need to create a group. You can do this with the following code:

// Create Array to Contain Objects data = ODT.Data.AddGroup(“TestData”); arr = data.AddVarOfArrayType(“FontArray”);

You can then append objects to the array using the following:

// Populate array with Object1 obj = arr.AddItemOfClassType(“TestClass”); obj.username = “felix”; obj.password = “mypassword”; EnableAllMethods(obj); // Populate array with Object2 obj = arr.AddItemOfClassType(“TestClass”); obj.username = “frank”; obj.password = “mypass”; EnableAllMethods(obj);

So how do we use this array of objects? Unfortunately I couldn’t find a straight forward way of doing this. TestComplete seems to use some kind of built in loop which can be activated using the .Run() method. For this to work we need to add a method to the object’s class and create a function for this method to link to; additionally we need to ‘enable’ the method so that we be executed. This is how I set it up (note that this is still quite messy and is just to display the concept):

function test(){ //Clean Up ODT.Data.Clear(); ODT.Classes.Clear(); // Create Class create_class(); // Create Array to Contain Objects create_array(); //Loop through data ODT.Data.TestData.Run(); } function create_class(){ // Create Class TestClassObj = ODT.Classes.Declare(“TestClass”); TestClassObj.AddMethod(“disp”, “Unit3.stub”); TestClassObj.AddProperty(“username”, “”); TestClassObj.AddProperty(“password”, “”); } function create_array(){ // Create Array to Contain Objects data = ODT.Data.AddGroup(“TestData”); arr = data.AddVarOfArrayType(“FontArray”); // Populate array with Object1 obj = arr.AddItemOfClassType(“TestClass”); obj.username = “felix”; obj.password = “mypassword”; EnableAllMethods(obj); // Populate array with Object2 obj = arr.AddItemOfClassType(“TestClass”); obj.username = “frank”; obj.password = “mypass”; EnableAllMethods(obj); } function stub(){ } // Enables all the methods of the specified ODT class instance so that all of them are executed function EnableAllMethods(variable){ for (var i = 0; i < variable.MethodCount; i++) variable.Methods(i).Enabled = true;}

If you put a breakpoint inside the stub() function you should see that it is called twice since we have two objects in the object array. Try changing the number of objects to confirm this relationship.
Ok, so the loop appears to be working, but how to access the information in the object? To do this we need to use the ‘This’ keyword as TestComplete automatically loops through the objects. ‘This’ means we are referring to the object of the current iteration. I put the following into the stub() function to test it out:

Log.Message(“Username = “ + This.username + “, Password = “ + This.password);

Hopefully you see the username and password displayed for both the objects:

Storing the Excel Data in an Object Array:

NOTE: remember that my Aliases names may be different to yours. 
The aliases are stored in the NameMapping file.

function main(){ //Clean Up ODT.Data.Clear(); ODT.Classes.Clear(); // Create Class create_class(); // Create Array to Contain Objects data = ODT.Data.AddGroup(“TestData”); arr = data.AddVarOfArrayType(“FontArray”); // Create Excel Driver // If you connect to an Excel 2007 sheet, use the following method call: DDT.ExcelDriver(“C:\\MyFile.xlsx”, “Sheet1”, true); Driver = DDT.ExcelDriver(“C:\\Users\\USER\\Desktop\\MyFile.xls”, “Sheet1”); // Iterates through records RecNo = 0; while (!Driver.EOF() ) { obj = arr.AddItemOfClassType(“TestClass”); obj.username = aqConvert.VarToStr(DDT.CurrentDriver.Value(0)); obj.password = aqConvert.VarToStr(DDT.CurrentDriver.Value(1)); EnableAllMethods(obj); Driver.Next(); // Goes to the next record } // Closes the Excel driver DDT.CloseDriver(Driver.Name); //Loop through data ODT.Data.TestData.Run();} function create_class(){ // Create Class TestClassObj = ODT.Classes.Declare(“TestClass”); TestClassObj.AddMethod(“disp”, “Combined.test1”); TestClassObj.AddProperty(“username”, “”); TestClassObj.AddProperty(“password”, “”);} function test1(){ var URL = http://support.smartbear.com/samples/testcomplete/weborders/ Browsers.Item(btFirefox).Run(); //Launches the specified browser. Browsers.CurrentBrowser.Navigate(URL); //Opens the specified URL in a running instance of the specified browser. // Check if logged-in an logout if necessary if (SearchBody(“Logout”)){ Logout(); } //Clicks at point (10, 5) of the ‘textboxMaincontentUsername’ object. Aliases.browser.pageWebOrdersLogin.formAspnetform.panelLogin.textboxMaincontentUsername.Click(-1, 1); //Sets the text ‘Tester’ in the ‘textboxMaincontentUsername’ text editor. Aliases.browser.pageWebOrdersLogin.formAspnetform.panelLogin.textboxMaincontentUsername.SetText(This.username); //Enters ‘[Tab]’ in the ‘textboxMaincontentUsername’ object. Aliases.browser.pageWebOrdersLogin.formAspnetform.panelLogin.textboxMaincontentUsername.Keys(“[Tab]”); //Sets the text ‘test’ in the ‘passwordboxMaincontentPassword’ text editor. Aliases.browser.pageWebOrdersLogin.formAspnetform.panelLogin.passwordboxMaincontentPassword.SetText(This.password); //Enters ‘[Enter]’ in the ‘passwordboxMaincontentPassword’ object. Aliases.browser.pageWebOrdersLogin.formAspnetform.panelLogin.passwordboxMaincontentPassword.Keys(“[Enter]”); //Closes the ‘BrowserWindow’ window. Aliases.browser.BrowserWindow.Close(); while (Sys.WaitBrowser().Exists){ ; }} function Logout(){ //Clicks the ‘linkLogout’ control. Aliases.browser.pageWebOrders.formAspnetform.linkLogout.Click(); //Waits until the browser loads the page and is ready to accept user input. Aliases.browser.pageWebOrdersLogin.Wait();} function SearchBody(str){ var browser = Sys.Browser(“*”); var page = browser.Page(“*”); obj = page.NativeWebObject.Find(“contentText”, str, “A”); if (obj.Exists) return 1; else return 0;} // Enables all the methods of the specified ODT class instance so that all of them are executed function EnableAllMethods(variable){ for (var i = 0; i < variable.MethodCount; i++) variable.Methods(i).Enabled = true;}
Advertisements