Creating Scripting Objects From Delphi Application Objects

Applies to TestComplete 14.30, last modified on November 21, 2019
Information in this topic applies to desktop applications only.
Connected and Self-Testing applications are deprecated. These technologies will be removed in one of the future releases of TestComplete.
To create test code that runs from within your tested apps, use TestLeft, a SmartBear functional testing tool for developers.

This topics explains how you can expose objects of Delphi applications as scripting objects in TestComplete. The explanation applies to applications created using the following compilers:

  • Borland Delphi 7.0
  • Borland Delphi 2005 and 2006 for Win32
  • CodeGear Delphi 2007 and 2009 for Win32
  • Embarcadero Delphi 2010 and XE

The topic is divided into the following sections:

Preparing Your Delphi Project

In order for you to expose objects of Delphi applications as TestComplete scripting objects, you need to get access to the TestComplete COM server. The easiest way to manage TestComplete via COM is to transform your application into a Connected Application. Delphi Connected Applications are created by linking the <TestComplete>\Connected Apps\Delphi\TCConnect.pas unit to your project. This unit provide objects and routines that free you from having to manually write the code needed to connect to TestComplete via COM.

For detailed instructions on creating Delphi Connected Applications, see Creating Connected Applications in Delphi.

Creating the Object Type Library

TestComplete can create scripting objects from those objects of Delphi applications that provide type information. So, before creating a custom scripting object and writing its code, you need to create a type library that describes the object’s interface, its methods and properties, their parameters and so on. If you need to expose an existing object of an application (for instance, a specific global or non-visual object), you need to create a COM wrapper that provides access to this object. To learn how you can do this, please refer to the Delphi documentation.

To create a new type library in Delphi, follow these steps:

  • Open your project in Delphi.

  • Choose File | New | Other from Delphi’s main menu. This will call the New Items dialog.

  • In Delphi 7, switch to the ActiveX page of the dialog, select Type Library and press OK.

    In Delphi 2005 or 2006 for Win32, select the Delphi Projects | ActiveX category on the left of the dialog, then select Type Library and press OK.

    Delphi will create a new type library and show the editor for it.

  • Using the editor, create an interface that defines your scripting object. In our example, we will create an interface containing one read-write property, Text, and one method, ShowText:

    Delphi Type Library Editor
    Tip: Help strings that you specify for methods and properties will be shown as a method (or a property) description in the Code Completion window.
  • Select File | Save from Delphi’s main menu.

    Delphi will generate the binary type library file (.tlb) and the LibraryName_TLB.pas unit holding the type library implementation. It will also add this unit to your project.

  • Exit the Type Library editor.

Writing the Object Code

After you have defined the object interface, you can create a class that implements this interface and write the implementation of the interface methods and properties. In our example, the Text property will obtain and return a text string, and the ShowText method will display this string in a message box.

The class name of our object to be exposed is TSampleRuntimeObject. We made the object class a descendant of the TAutoIntfObject class. This is a Delphi class implementing the IDispatch interface. Since TAutoIntfObject is defined in the ComObj unit, we include this unit into the uses clause of the unit. We also included the ActiveX unit there, because it holds the definition of the ITypeLib interface that provides access to the information on types in a type library (this interface is used in the TSampleRuntimeObject class constructor).

Delphi

{SampleRuntimeObjectUnit.pas}

unit SampleRuntimeObjectUnit;

interface
uses
  Dialogs, ComObj, ActiveX, SampleRuntimeObject_TLB;
type
  TSampleRuntimeObject = class(TAutoIntfObject, ISampleRuntimeObjectIntf)
  private
    FText: WideString;
    function Get_Text: WideString; safecall;
    procedure Set_Text(const Value: WideString); safecall;
  public
    constructor Create;
    procedure ShowText; safecall;
    property Text: WideString read Get_Text write Set_Text;
end;

implementation

function GetTypeLib: ITypeLib;
begin
  OleCheck(LoadTypeLib(PWideChar(WideString(ParamStr(0))), Result));
end;

constructor TSampleRuntimeObject.Create;
begin
  inherited Create(GetTypeLib, ISampleRuntimeObjectIntf);
  FText := '';
end;

function TSampleRuntimeObject.Get_Text: WideString; safecall;
begin
  Result := FText;
end;

procedure TSampleRuntimeObject.Set_Text (const Value : WideString); safecall;
begin
  FText := Value;
end;

procedure TSampleRuntimeObject.ShowText; safecall;
begin
  ShowMessage(FText);
end;

end.

Registering and Unregistering Custom Scripting Objects

In order to add our custom object to the TestComplete object model, you need to register it in the TestComplete extensibility manager (see Creating Scripting Objects From Application Objects). In Delphi Connected Applications, you can obtain this manager using the Manager function (it is defined in TCConnect.pas).

To register a custom scripting object, use the manager’s AddName method. The method call can be inserted in the application’s initialization routine, or in a routine that is called upon a specific event. In our example, we will add a button control to the application form and place the registration code in the button’s OnClick event handler. This way, we will be able to easily re-register our object in subsequent TestComplete sessions while the application is running.

The sample code is provided below. It requires a form that has the class name TForm1 and contains a button named Button1.

Delphi

{Main.pas}

unit Main;

interface

uses
  Windows, SysUtils, Classes, Controls, Forms, Dialogs, StdCtrls,
  TCConnect, RuntimeObjectUnit, ComObj, ActiveX;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    FRuntimeObject: TSampleRuntimeObject;
    FRegistered : bool;
    const FRuntimeObjectName : WideString = 'SampleRuntimeObject';
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FRuntimeObject := TSampleRuntimeObject.Create;
  FRegistered := False;
end;

procedure TForm1.Button1Click(Sender: TObject);
const
  ProjectPath = 'TCProject\SampleRuntimeObject.pjs';
begin
  try
    // Open a project suite in TestComplete
    TC.Visible := True;
    TC.Integration.OpenProjectSuite(aqFileSystem.ExpandFileName(ProjectPath));
    // Register FRuntime object as a scripting object
    Manager.AddName(FRuntimeObjectName, False, FRuntimeObject as IDispatch);
    FRegistered := True;
  except
    on E: Exception do ShowMessage('Count not register ' + FRuntimeObjectName + ':'#13#10 + E.Message);
  end;
end;

end.

To unregister an object, use the RemoveName method of the TestComplete extensibility manager. In our example, we will place the unregistration code in the form’s Close event handler, so that the object is unregistered when the user exits the application by closing the form. Since the unregistration makes sense only if the object has been previously registered and if TestComplete is running, we check both these conditions before calling the RemoveName method:

Delphi

{Main.pas}

type
  TForm1 = class(TForm)
  ...
  procedure FormDestroy(Sender: TObject);
  ...
end;

procedure TForm1.FormDestroy(Sender: TObject);
const TCClassName = 'TestComplete.TestCompleteApplication.14';
begin
  if FRegistered and (GetActiveOleObject(TCClassName) <> nil) then
    Manager.RemoveName(FRuntimeObjectName);
end;

Finally, you can re-compile your application.

Working With Custom Scripting Object

After a custom scripting object is registered in TestComplete, it is displayed in the Code Completion window and can be used in scripts:

The SampleRuntimeObject object

You can get and set the object’s property values, call its methods and check their return values the same way you would with standard scripting objects such as TestedApps, Log and others. The only difference is that the processing is performed by your application rather than by TestComplete.

The code snippet below demonstrates how you can work with the sample scripting object we have created earlier in this topic. This object is available in TestComplete from the time you launch the application and click the button on its main form and until the application is closed. Note that, despite the object is a part of a Delphi application, it can be used in scripts written in any scripting language, not only DelphiScript:

JavaScript, JScript

function Main()
{
  SampleRuntimeObject.Text = "Hello, world!";
  SampleRuntimeObject.ShowText();
}

Python

def Main():
  SampleRuntimeObject.Text = "Hello, world!"
  SampleRuntimeObject.ShowText()

VBScript

Sub Main
  SampleRuntimeObject.Text = "Hello, world!"
  SampleRuntimeObject.ShowText
End Sub

DelphiScript

procedure Main;
begin
  SampleRuntimeObject.Text := 'Hello, world!';
  SampleRuntimeObject.ShowText;
end;

C++Script, C#Script

function Main()
{
  SampleRuntimeObject["Text"] = "Hello, world!";
  SampleRuntimeObject["ShowText"]();
}

See Also

Creating Scripting Objects From Application Objects
Connected Applications - Overview
Creating Connected Applications in Delphi

Highlight search results