scottb
12 years agoContributor
Exception handling: Change from JScript to Delphiscript?
I have a quick question about exception handling in Delphiscript.
I recently inherited about 2,000 KDT scripts with about 100 simple VBScript support functions. I translated all of the VBScripts to JScript because I am working on a richer and more powerful set of support functions. I am also doing a GUI map pilot. I chose JScript because it seemed like it would do better exception handling.
After spending a couple of weeks with JScript it turns out that JScript does not support excections between units. This is something that should have been documented better in the TC docs, like maybe in the Language Reference where everyone looks for technical info about a language instead of buring that critical fact deep inside the other docs. Ahem!
I spent enough time to build a proof of concept using JScript. But to get it to handle exceptions I had to throw a bunch of kludges into my scripts. I wrote one function to test whether a returned object looks like an exception. It is placed into a "common" unit and included in all of my scripts. I wrote a trap function that uses the previous one to determine if an object is an exception and if it is it throws an exception in the local unit. I place a copy of this in the top of all of my scripts. I place every statement that could produce an exception into the argument for the trap function. Every place where I would normally throw an exception, I return an Error instead. This adds to maintainance costs, and the code just looks stupid.
----
Unit GUIMAP
//USEUNIT Common
//USEUNIT GUIMapCommon
... etc.
function trap(result) {
var exptn;
if (isException(result)) {
exptn = new Error();
exptn.name = Common.tErr.name;
exptn.number = Common.tErr.number;
exptn.message = Common.tErr.message;
Common.tErr = null;
throw exptn;
}
return result;
}
... etc.
function PersonForm() {
var fnName = arguments.callee.toString().match(/^function ([^\(]+)/)[1];
var win;
var msgStr;
try {
NameMapping.Sys.MYAPPSuite.Refresh();
win = NameMapping.Sys.MYAPPSuite.frmFPCARApplication.MDIClient.fBasePerson.GetUnderlyingObject();
if (!win.Exists) throw new Error(fnName+": Unable to get object NameMapping.Sys.MYAPPSuite.frmMYApplication.MDIClient.fBasePerson");
trap ( this.WinButton = new FormWinButtons(win) );
// AltTextField() is in unit GUIMapCommon.
trap ( this.Address = new AltTextField(win,"~a") );
trap ( this.Email = new AltTextField(win,"~p~e") );
trap ( this.FirstName = new AltTextField(win,"~p~i") );
trap ( this.Gender = new AltComboBox(win, "~g") );
trap ( this.LastName = new AltTextField(win,"~p~s") );
... etc.
} catch (e) {
msgStr = "GUIMAP."+fnName+": "+formattedError(e);
Log.Warning(msgStr);
return new Error(msgStr);
}
}
---
UNIT MAIN
//USEUNIT Common
//USEUNIT GUIMap
... etc.
function trap(result) {
var exptn;
if (isException(result)) {
exptn = new Error();
exptn.name = Common.tErr.name;
exptn.number = Common.tErr.number;
exptn.message = Common.tErr.message;
Common.tErr = null;
throw exptn;
}
return result;
}
... etc.
function CreatePersonUsingAllFields(doTranDiff, doTranDiffUpdate) {
var fnName = arguments.callee.toString().match(/^function ([^\(]+)/)[1];
var testFinished=false;
... etc.
try {
... etc.
Log.Message(fnName+": Entering data into the required fields.");
try {
trap ( MYAPP.PersonForm.FirstName.Write(tFirstName) );
trap ( MYAPP.PersonForm.LastName.Write(tLastName) );
} catch(e) {
errMsg = fnName+": Error populating person form fields. "+formattedError(e);
throw new Error(9991,errMsg);
}
Log.Message(fnName+": Entering data into the nonrequired text fields.");
try {
trap ( MYAPP.PersonForm.Prefix.Write(tNamePrefix) );
trap ( MYAPP.PersonForm.MiddleInitial.Write(tMiddleInitial) );
trap ( MYAPP.PersonForm.Suffix.Write(tNameSuffix) );
trap ( MYAPP.PersonForm.Address.Write(tAddress) );
trap ( MYAPP.PersonForm.Email.Write(tEmailAddress ) );
trap ( MYAPP.PersonForm.Gender.Write(tGender) );
trap ( MYAPP.PersonForm.Birthdate.Write(tBirthDay) );
... etc.
} catch(e) {
Log.Error(fnName+": Error populating person form fields. "+formattedError(e));
}
testFinished=true;
} catch(e) {
testFinished=false;
Log.Error(fnName+": Fatal error. "+formattedError(e));
} finally {
if (!testFinished)
Log.Error(fnName+": Test failed. Not all test points were executed.");
}
Now I am looking into translating all of my JScript stuff into Delphiscript. I have three issues here.
First, I already changed about a hundred mostly small function from VBScript to JScript. I don't want to commit to Delphiscript and find out partway in that Delphiscript has a fatal issue like JScript does. Is anyone aware of any "gotchas" with Delphiscript, especially with respect to exception handling in a complex scripts?
Second, I have to explain to my management why I should spend resources moving from JScript to Delphiscript after I already spent a lot of resources for moving from VBScript to JScript. Are there any other advantages to using Delphiscript besides just cleaning up the exception handling? Isn't Delphiscript something Smart Bear created, and wouldn't it be "closer to the metal" than tacked on languages such as JScript and VBScript, and therefore more likely to work well with the TestComplete application?
Third, I'm guessing that this is maybe a typo, the TestComplete documentation discussing ODT has a section on "Creating Classes Programmatically," and "Creating Objects Programmatically," and their examples demonstrate how to use ODT methods in Visual Basic, Java, Delphi, and C# directly. Can I really work with TestComplete methods through those languages? If I can, is it possible to do all of my testing using Java or C# instead of one of the scripting languages? I would prefer to work with a regular programming language, especially Java, instead of one of the scripting languages.
By the way, my primary test application is a Delphi app. It is supplemented with a Flex based web app.
Your feedback will be greatly appreciated.