AutomatedQA: Award-winning tools for software testing and quality assurance

Home » Products » TestComplete » FAQs » TestComplete FAQ - Writing Script Code

TestComplete 6 FAQ - Writing Script Code

This page contains answers to frequently asked questions about TestComplete ver. 4 - 6. For answers to questions on TestComplete 3, see TestComplete 3 FAQ.



Q.: How can I call procedures or variables declared in another unit? I need to call a procedure declared in UnitA from UnitB and vice versa.

A.: By default, a script can call routines declared within the same unit. To call routines in another unit, the current one must be “linked” to it:

In VBScript projects place the cursor at the beginning of the unit and add the following string:

'USEUNIT Unit_Name

Be sure you specify the apostrophe ( ' ) before USEUNIT.

To link to several units, use the following syntax:

'USEUNIT Unit1_Name
'USEUNIT Unit2_Name

In DelphiScript projects place the cursor at the beginning of the unit and add the following string:

uses Unit_Name;

If you need to link several units, separate their names with commas, that is

uses Unit1_Name, Unit2_Name;

In JScript, C++Script or C#Script projects place the cursor at the beginning of the unit and add the following string:

//USEUNIT Unit_Name;

Be sure to specify the double slash ( // ) before USEUNIT.

You can link several units in the following manner:

//USEUNIT Unit1_Name
//USEUNIT Unit2_Name

Note: The USEUNIT statement does not allow tab or space characters at the beginning of the line and between the USEUNIT and the comment operator (' in VBScript and // in JScript).

Units can refer to each other. In other words, circular references are allowed. For instance, if you need to call scripts declared in UnitA from UnitB, and to call scripts declared in UnitB from UnitA, you must add references to both units:

[VBScript]
UnitA
'USEUNIT UnitB
...
 
UnitB
'USEUNIT UnitA
...

[DelphiScript]

UnitA
 uses UnitB;
...

UnitB
  uses UnitA;
...

Circular references are not allowed in JScript, C++Script and C#Script. In these languages, circular links may cause errors in the jscript.dll (C++Script and C#Script are based on JScript and use this dll as well), which causes errors in TestComplete.

Another solution is to use the Runner.CallMethod function. It calls any routine specified by its full name (that is, unit_name.routine_name). If you call a script routine using this function, there is no need to include the unit specified by unit_name in the uses (USEUNIT) clause of the current unit.

[VBScript]
Call Runner.CallMethod("UnitA.MyScript")

[JScript]
Runner.CallMethod("UnitA.MyScript")

[DelphiScript]
Runner.CallMethod('UnitA.MyScript)

[C++Script, C#Script]
Runner["CallMethod"]("UnitA.MyScript")

Back to list

Q.: I can use the same name for two procedures that are in different units, because I cannot use a prefix that would specify the unit. Is this correct?

A.: You can use the same name for the routines that are in different units. To distinguish routines use the unit name as a prefix, that is you can use the syntax unit_name.script_name to call a script routine. In earlier versions of TestComplete this approach only worked for DelphiScript, but now works for all scripting languages.

Back to list

Q.: Is there a way to call routines defined in another project?

A.: Yes, it’s possible. If you have several test projects, you may want to use the same routines from one project in other ones. The uses (or USEUNIT) clause added to a unit, lets you refer to routines defined in other units of the same project. If you need to call routines defined in a unit from another project, you should add a unit that holds these routines to your project. To do this, right-click the Script project item in the Project Explorer panel and choose Add | Existing Item from the context menu. This will display the standard Open File dialog where you can specify the unit to be added.

If the unit includes routines that you do not want to “export”, you can create a new unit by selecting New Unit on the Units toolbar, copy the needed routines to this unit and then add the new unit to the desired project. You can even create a unit “library”, that is, units that hold the desired routines, and then add these units to any project you want.

Back to list

Q.: I cannot find functions to deal with time. Where should I search for them?

A.: VBScript and JScript have special objects and functions that return time, e.g. Time or Now in VBScript or the Date object in JScript. Since C++Script and C#Script are based on JScript, you can use JScript functions in these languages as well.

If you use DelphiScript, you can use the following routines from the Utilities plug-in (it is installed in the <TestComplete>\Extensions folder) --

DateTimeToStr
StrToDateTime
Now
Date
Time


See the “Utilities Object” help topic for more information.

For example:

Log.Message(Now)
Log.Message(DateTimeToStr(Now))
Log.Message(DateToStr(Now))
Log.Message(TimeToStr(Now))

You can also use functions from this plug-in in any other scripting language, not only in DelphiScript.

Another scripting object - StopWatch, which is available in TestComplete 5 and later, lets you measure time intervals during the script execution:

HISUtils.StopWatch.Start();
Test();    // run the test
HISUtils.StopWatch.Stop();
Log.Message("Test execution time: " + HISUtils.StopWatch.ToString());

For more information, see “StopWatch Object” in TestComplete on-line help.

You can find many examples on working with date and time values in the “Working With Dates” and “Working With Time” sections in TestComplete’s help file.

Back to list

Q.: How can I pass function parameters by reference in JScript (C++Script, C#Script)?

A: JScript does not support passing function parameters by reference. Since C++Script and CScript are based on JScript, they do not support reference parameters as well. However, there are several approaches that you can use to work around this restriction:

1. Use global variables. Global variables can be accessed and modified throughout the entire script unit:

[JScript]
var MyVar;

function Test()
{
  MyVar = 10;
  ChangeGlobalVariable();
  Log.Message(MyVar);  // 12
}

function ChangeGlobalVariable()
{
  MyVar += 2;
}

[C++Script, C#Script]
var MyVar;

function Test()
{
  MyVar = 10;

  ChangeGlobalVariable();
  Log["Message"](MyVar);  // 12
}

function ChangeGlobalVariable()
{
  MyVar += 2;
}

2. Use project, project suite or network suite variables. These variables are accessible and can be modified in all scripts in the current project, project suite or network suite, respectively (for more information on these variables, see the “Local Variables” topic in TestComplete help):

[JScript]
function Test()
{
  Project.Variables.MyVar = 10;
  ChangeProjectVariable();
  Log.Message(Project.Variables.MyVar);  // 12
}

function ChangeProjectVariable()
{
  Project.Variables.MyVar += 2;
}

[C++Script, C#Script]
function Test()
{
  Project["Variables"]["MyVar"] = 10;
  ChangeProjectVariable();
  Log["Message"](Project["Variables"]["MyVar"]);  // 12
}

function ChangeProjectVariable()
{
  Project["Variables"]["MyVar"] += 2;
}

3. Return the desired value from the function using the return operator and assign it to the variable:

[JScript]
function Test()
{
  var MyVar = 10;
  MyVar = GetNewValue(MyVar);
  Log.Message(MyVar);  // 12
}

function GetNewValue(AVar)
{
  return AVar + 2;
}

[C++Script, C#Script]
function Test()
{
  var MyVar = 10;
  MyVar = GetNewValue(MyVar);
  Log["Message"](MyVar);  // 12
}

function GetNewValue(AVar)
{
  return AVar + 2;
}

4. The JScript Array and Object objects are always passed by reference, so changes made to array elements and object properties remain after the execution returns to the caller function:

[JScript]
function Test()
{
  var MyArray = new Array(1);
  MyArray[0] = 10;
  ChangeArray(MyArray);
  Log.Message (MyArray[0]);  // 12

  var MyObj = new MyObject (20);
  ChangeObject (MyObj);
  Log.Message (MyObj.MyValue);  // 22
}

function ChangeArray(Arr)
{
  Arr[0] += 2;
}

// Creates a new MyObject object
function MyObject (AVal)
{
  this.MyValue = AVal;
}

function ChangeObject (AObj)
{
  AObj.MyValue += 2;
}

[C++Script, C#Script]
function Test()
{
  var MyArray = new Array(1);
  MyArray[0] = 10;
  ChangeArray(MyArray);
  Log["Message"](MyArray[0]);  // 12

  var MyObj = new MyObject (20);
  ChangeObject (MyObj);
  Log["Message"](MyObj["MyValue"]);  // 22
}

function ChangeArray(Arr)
{
  Arr[0] += 2;
}

// Creates a new MyObject object
function MyObject (AVal)
{
  this["MyValue"] = AVal;
}

function ChangeObject (AObj)
{
  AObj["MyValue"] += 2;
}

Back to list

Q.: How can I close a modal dialog which was invoked by a method called from script?

A.: Generally a script instruction is not executed until the previous instruction is finished. This behavior causes unexpected pauses in test execution since routines that invoke modal dialogs return the resulting value only when the corresponding dialog is closed.

To solve this issue the Runner.CallObjectMethodAsync method was introduced in TestComplete ver. 4.2. It calls the specified object method asynchronously. That is, it does not wait for the method to complete, but proceeds to the next code instruction. Thus the further instructions can simulate the actions needed to close the dialog (typically press OK or Close button). To find out whether the previously called method had finished, the CallObjectMethodAsync returns an especial CallObjectMethodAsyncResult object. See the "Testing Modal Windows" help topic for details.

Below is an example that programmatically calls a dialog of an Open application and afterwards closes it via user interface actions.

[VBScript]

Sub Test
  Dim p, w, ResObject
  
  ...
      
  ' Calls the method that displays a modal dialog
  Set ResObject = Runner.CallObjectMethodAsync(p.VCLObject("MainForm").VCLObject("ADialog"), "Execute")
  
  ' Simulates user actions over the dialog and closes it
  Set w = p.WaitWindow("*", " ADialogCaption ", -1, 1000)
  If w.Exists Then
    w.Window("Button", "*Ok*").ClickButton()
  Else
    Log.Message("The window was not found.")
  End If

  ' Waits for method completion
  ResObject.WaitForCompletion(5000)
  ' Processes the result
 If ResObject.Completed Then
    Log.Message("Success, Return Value: "+CStr(ResObject.ReturnValue))
 Else
    Log.Message("Failure, Return Value: "+CStr(ResObject.ReturnValue))
 End If
  ...
End Sub

[JScript]

function Test()
{
  var p, w, ResObject;
  
  ...
      
  // Calls the method that displays a modal dialog
  ResObject = Runner.CallObjectMethodAsync(p.VCLObject("MainForm").VCLObject("ADialog "), "Execute");
  
  // Simulates user actions over the dialog and closes it
  w = p.WaitWindow("*", " ADialogCaption", -1, 1000);
  if (w.Exists)
    w.Window("Button", "*Ok*").ClickButton();
  else Log.Message("The window was not found.")

  // Waits for method completion
  ResObject.WaitForCompletion(5000);
  // Processes the result
  if(ResObject.Completed)
    Log.Message("Success, Return Value: " + ResObject.ReturnValue);
  else
    Log.Message("Failure, Return Value: " + ResObject.ReturnValue);
  
  ...
}

[DelphiScript]

procedure Test();
var p, w, ResObject: OleVariant;
begin
  
  ...
      
  // Calls the method that displays a modal dialog
  ResObject := Runner.CallObjectMethodAsync(p.VCLObject('MainForm').VCLObject('ADialog'), 'Execute');
  
  // Simulates user actions over the dialog and closes it
  w := p.WaitWindow('*', 'ADialogCaption', -1, 1000);
  if w.Exists then 
    w.Window("Button", '*Ok*').ClickButton()
  else Log.Message('The window was not found.');

  // Waits for method completion
  ResObject.WaitForCompletion(5000);
  // Processes the result
  if ResObject.Completed then
    Log.Message('Success, Return Value: ' + VarToStr(ResObject.ReturnValue))
  else
    Log.Message('Failure, Return Value: ' + VarToStr(ResObject.ReturnValue));
    
  
  ...
end;

[C#Script, C++Script]
function Test()
{
  var p, w, ResObject;
  
  ...
      
  // Calls the method that displays a modal dialog
  ResObject = Runner["CallObjectMethodAsync"](p["VCLObject"]("MainForm")["VCLObject"]("ADialog"), "Execute");
  
  // Simulates user actions over the dialog and closes it
  w = p["WaitWindow"]("*", "ADialogCaption", -1, 1000);
  if (w["Exists"])
    w["Window"]("Button", "*Ok*")["ClickButton"]();
  else Log["Message"]("The window was not found.")

  // Waits for method completion
  ResObject["WaitForCompletion"](5000);
  // Processes the result
  if(ResObject["Completed"])
    Log["Message"]("Success, Return Value: " + ResObject["ReturnValue"]);
  else
    Log["Message"]("Failure, Return Value: " + ResObject["ReturnValue"]);
  
  ...
}

However, it is not recommended to use the Runner.CallObjectMethodAsync method to perform asynchronous calls to methods provided by TestComplete. Some of the built-in methods may operate normally, while the others may cause problems when they are called asynchronously. Therefore certain TestComplete methods cannot be launched via CallObjectMethodAsync. An example of such a method is the BuiltIn.SendMAPIMail routine. It often invokes a security warning dialog displayed by Outlook / Outlook Express which cannot be closed by TestComplete.

The only solution is to close the dialog from another process, namely, via the Windows Script Host. This is a language-independent utility that introduces wide capabilities for automation. The helper script we are interested in should focus the dialog window and perform the actions needed to close the dialog.

Here is the code of a script that closes the Outlook Express security dialog. The code should be placed in a text file named CloseSecurityDialog.vbs

' VBScript

WindowCaption = "Outlook Express"
Set WshShell = WScript.CreateObject("WScript.Shell")
' Focus the dialog window a = WshShell.AppActivate(WindowCaption)
While Not a
  WScript.Sleep(100)
  a = WshShell.AppActivate(WindowCaption)
WEnd
WScript.Sleep(600)
' Simulate the ALT+S keystroke that is an accelerator For the "Send" button
WshShell.SendKeys("%S")

Once the helper Windows Script is created, you can call it from TestComplete right before the instruction that invokes the modal dialog. The following code snippet demonstrates how to do this:

[VBScript]

Dim WshShell
  ...
  ' Obtain the OLE object of Windows Script Host
  Set WshShell = Sys.OleObject("WScript.Shell")
  ' Launch the script at specified path
  WshShell.Run("C:\Work\CloseSecurityDialog.vbs")

  ' Call the routine invoking a modal dialog
  If SendMAPIMail("John Smith", "jsmith@domain.com", "MessageSubject", "MessageBody") Then
    Log.Message("Mail was sent")
  Else
    Log.Warning("Mail was not sent")
  End If
  ...
End Sub

[JScript]
function ModalMessager()
{
var WshShell;
  ...
  // Obtain the OLE object of Windows Script Host
  WshShell = Sys.OleObject("WScript.Shell");
  // Launch the script at specified path
  WshShell.Run("C:\\Work\\CloseSecurityDialog.vbs");

  // Call the routine invoking a modal dialog
  if (SendMAPIMail("John Smith", "jsmith@domain.com", "MessageSubject", "MessageBody"))
    Log.Message("Mail was sent");
  else
    Log.Warning("Mail was not sent");
  ...
}

[DelphiScript]
function ModalMessager();
var WshShell : OleVariant;
begin
  ...
  // Obtain the OLE object of Windows Script Host
  WshShell := Sys.OleObject('WScript.Shell');
  // Launch the script at specified path
  WshShell.Run('C:\Work\CloseSecurityDialog.vbs');

  // Call the routine invoking a modal dialog
  if (SendMAPIMail('John Smith', 'jsmith@domain.com', 'MessageSubject', 'MessageBody')) then
    Log.Message('Mail was sent.')
  else
    Log.Warning('Mail was not sent.');
  ...
end;

[C#Script, C++Script]
function ModalMessager()
{
var WshShell;
  ...
  // Obtain the OLE object of Windows Script Host
  WshShell = Sys["OleObject"]("WScript.Shell");
  // Launch the script at specified path
  WshShell["Run"]("C:\\Work\\CloseSecurityDialog.vbs");

  // Call the routine invoking a modal dialog
  if (SendMAPIMail("John Smith", "jsmith@domain.com", "MessageSubject", "MessageBody"))
    Log["Message"]("Mail was sent");
  else
    Log["Warning"]("Mail was not sent");
  ...
}

Back to list

Copyright © 1999-2008, AutomatedQA, Corp. All Rights Reserved.
Home | Legal | About | Contact | Site Map | Print