Forum Discussion
- mburrOccasional ContributorWished there was a checkbox option for ODT in the 10.40 install but I did not see it. The oldest project I have is written in VBScript and since I like OO I used ODT. I have since moved to JScript for later projects as it is easier but have not yet converted the one old project. I would definately vote to keep ODT. It will be awhile before I convert 27,000 lines of VBScript into JScript. I will need the interim ODT plugin until then.
- Manfred_FRegular ContributorWe, too, want to KEEP ON USING ODT.
Maybe, it's "rarely used", because, maybe, it is not the simplest Approach. But, if You know how to apply it, it is much better than spaghetti Scripting.
Here is, how I use it:
Normal procedural test scripts tend to have a poor, spaghetti-like structure. There are long routines, and at a given code location it is not clear, which test step and/or test case is implemented there. Test data and reference data are often defined in the code as magic numbers and strings.
Using ODT, I can do it better: the “script” is clearly divided into sections (Abschnitte), sequences and steps (Testschritte), see the attached screenshot of ODT data with the structure highlighted in green.
Please notice the green rectangle: item0 and item1 hold test case references to the test step. This is a perfect way to integrate metadata. I could easily add a script to evaluate them, e.g. to generate a table or list containing each step and its implemented test cases. In a standard test script, I’d have to use code patterns to integrate metadata and I’d have to do code analysis to evaluate, which is far less comfortable and maintainable.
The test steps could have names, but I’ve left that out, so they are only identified via their sequence and hierarchical position – like it was in my original test script for manual testing. However, each test step calls an ODT test function, which I gave a name to like Test_2_1_2(), containing the original test step ID.
So, for each ODT test script code location, it is clear a) which test step it serves for (from the routine name) and b) which test cases are implemented.
In addition, It is perfectly easy to add setup and shutdown functionality on each level of the test object hierarchy, and it leads to transparent results.
To separate test and reference data form the scripts, I use the StdDatum data elements (highlighted in blue) integrated in the Daten_Test and Daten_Ref arrays of test elements on every level of hierarchy. The upper example shows a dialog that is to be opened by menu “Einrichten|BDE-Recorder”. In the lower example, a date value is to be set for field “Auflösung”.
Accessing the ODT data tree originally is not so very comfortable, You have to deal with all these .owner, .properties and .value elements. So, to facilitate using my data definitions, I use a function defined in a script extension to get hold of the test data. Calling “Td = PVA_0.ODT.TestDatenArr(This, 0)” in an ODT test routine gives me an array with a wrapper object for each StdDatum in the Daten_Test property of This object. Calling “TdP = PVA_0.ODT.TestDatenArr(This, 1)” does the same for the superordinate test data object, and so on.
The wrapper for StdDatum is a vbs object. It contains mostly getters/setters for the properties and also some very convenient procedures to use in ODT test procedure.
If I call “TdP(mcBdeTblDlgIx).DialogOpenByMenu” from the ODT test routine of step item1 , I access the dialog in the upper example. TdP(mcBdeTblDlgIx) gives me access to the test data element with the given index on parent-level. During initialisation, TC took the alias string from field ObjektDefinition and created an alias object from it and stored it into field “object”. Now, the DialogOpenByMenu routine opens the dialog per menu and waits until the alias object is ready (exists and visible) and creates and stores a wrapper object for the dialog into the Objekt field – ready to use.
Calling “Td(mcSchrAufloesungIx).SetText(myFrm)” sets in the lower example the text from the StdDatum property “Wert” to the field with caption “Auflösung”. The parameter myFrm holds a wrapper object for the frame carrying the target field, I get it from the wrapper object of the dialog.
So as a result – opening a dialog or setting a value can easily be defined in the data tree and executed in a ODT test function by a simple line of code. The test and reference data are located in the data tree, separated from the test script code. There they can be moved to the appropriate level in the test element hierarchy with little effort (copy & paste), the adaptions in the ODT code can easily be handled, if constants are used as index in the test data array (Td).
Furthermore, ODT is very useful to hold and evaluate metadata and for disposition of setup/shutdown functionality.
Regards,
Manfre - mburrOccasional ContributorI discovered once I migrated from ODT, (Object Driven Testing), to VB script classes, that the test code actually ran faster. The conversion ended up to be easier than I originally thought. This may be due in part to the fact that I mostly used ODT to create classes and arrays. Unfortunately, I also used the Owner.Owner stuff but that ended up to be easy to get rid of. I also used classes to drive UserForms and Network testing, but that ended up to be no problem either.
The tests we run are completely data driven using DDT with an excel spreadsheet. (Hope they don't get rid of that ;-) The test server that needed to be converted was completely written in VB but fortunately the test clients were written using jscript. The test server had over 40 ODT classes. Several classes were fairly large with 30+ properties and 20+ methods. A good number of methods would also use ODT.Classes.NewArray().
So, in order, here is a high level view of what I did. Of course I would continually test the test code after each change.
1. Got rid of all uses "Owner.Owner." Used module level variables and/or functions to pass data from a parent class to a child class.
2. Made sure the organization of routines was such that all routines for a class were co-located. This does not include UserForm event modules that need to be kept outside any class.
3. Defined a custom array class that for the most part would replace ODT.Classes.NewArray() directly. See below.
4. Converted and tested each class one at a time. To save some time I simply used the Public keyword for public variables instead of defining the full Let/Get definitions. (Let/Get are done under the hood anyway.) When you test each class you will most likely discover something you forgot to convert. Each class most likely will need an accessor function outside the class so other "units" can instantiate an object based on that class.
Once you have completed the conversion for all ODT classes and arrays, and your test code still run OK, you can remove ODT from your project.
Class CustomArray
' --- Private properties
Private m_Array()
Private m_Count
Private m_IsObject ' basic type of 1st item - object or non object
' --- Public Properties
Public Property Let Count(value)
m_Count = value
End Property
Public Property Get Count()
Count = m_Count
End Property
' Read only
Public Property Get IsObject()
IsObject = m_IsObject
End Property
' --- Private Methods
Private Function UseSet(ArrayItemToCheck)
Dim strTemp
Dim blnUnexpectType
blnUnexpectType = False
UseSet = False
Select Case VarType(ArrayItemToCheck)
Case 0
strTemp = "vbEmpty"
blnUnexpectType = True
Case 1
strTemp = "vbNull"
blnUnexpectType = True
Case 2
strTemp = "vbInteger"
Case 3
strTemp = "vbLong"
Case 4
strTemp = "vbSingle"
Case 5
strTemp = "vbDouble"
Case 6
strTemp = "vbCurrency"
Case 7
strTemp = "vbDate"
Case 8
strTemp = "vbString"
Case 9
strTemp = "vbObject"
UseSet = True
Case 10
strTemp = "vbError"
blnUnexpectType = True
Case 11
strTemp = "vbBoolean"
Case 12
strTemp = "vbVariant"
Case 13
strTemp = "vbDataObject"
UseSet = True
Case 14
strTemp = "vbDecimal"
Case 17
strTemp = "vbByte"
Case 8192
strTemp = "vbArray"
UseSet = True
Case Else
strTemp = "undetected: " & VarType(ArrayItemToCheck)
blnUnexpectType = True
End Select
If blnUnexpectType Then
Log.Error("Unexpected array item type: " & strTemp)
End If
End Function
Private Sub Class_Initialize()
m_Count = 0
m_IsObject = False
ReDim m_Array(-1)
'Log.Message("UBound=" & UBound(m_Array))
End Sub
' --- Public Methods
Public Function GetArray()
GetArray = m_Array
End Function
' Returns a specific array item
Public Default Function Items(index)
If index < m_Count And index > -1 Then
If m_IsObject Then
Set Items = m_Array(index)
Else
Items = m_Array(index)
End If
Else
Log.Error("index out of range")
End If
End Function
' Used to change value of an existing item
Public Function SetItem(index, value)
if m_IsObject Then
Set m_Array(index) = value
Set SetItem = m_Array(index)
Else
m_Array(index) = value
SetItem = m_Array(index)
End If
End Function
Public Function AddItem(value)
If m_Count = 0 Then
If UseSet(value) Then
m_IsObject = True
Else
m_IsObject = false
End If
Else
If UseSet(value) <> m_IsObject Then
Log.Error("Attempted to mix objects and non objects into the array. Item NOT added!")
Exit Function
End If
End If
ReDim Preserve m_Array(m_Count)
'Log.Message("UBound=" & UBound(m_Array))
if m_IsObject Then
Set m_Array(m_Count) = value
Set AddItem = m_Array(m_Count)
Else
m_Array(m_Count) = value
AddItem = m_Array(m_Count)
End If
m_Count = m_Count + 1
End Function
Public Sub DeleteItem(index)
Dim index2
If index < m_Count and index > -1 Then
If m_IsObject Then
Set m_Array(index) = Nothing
Else
m_Array(index) = vbNull
End If
if m_Count > 1 And index < (m_Count - 1) Then
' Collapse array at the element that is being removed
index2 = index + 1
While index2 < m_Count
If m_IsObject Then
Set m_Array(index2 -1) = m_Array(index2)
Else
m_Array(index2 -1) = m_Array(index2)
End IF
index2 = index2 + 1
Wend
'ElseIf index = 0 And m_Count = 1 Then ' We must be removing the 1st and only element
' Do nothing as the adjustment of m_Count and the ReDim will suffice
'Else ' We must be removing the last element
' Do nothing as the adjustment of m_Count and the ReDim will suffice
End If
m_Count = m_Count - 1
Redim Preserve m_Array(m_Count - 1)
'Log.Message("UBound=" & UBound(m_Array))
Else
Log.Error("index out of range")
End If
End Sub
End Class
Function NewArray()
Set NewArray = New CustomArray
End Function - karkadilValued ContributorJust a quick guess.
Is it possible that you didn't tick corresponding checkbox during the installation? - joseph_michaudModeratorNo, alas, support for ODT was removed in 10.40.
From the installed documentation:
The ODT plug-in is obsolete. We would recommend finding other ways to perform the tasks that you do with it. For instance, ODT is often used for creating custom classes (objects). You can define and create classes (objects) in JScript code by using JScript’s built-in functionality (see the JScript reference on the MSDN web site).
To keep your tests functioning during the transaction, you can request the ODT plug-in from our Support Team.
A support case has been opened and we're working on it there. - mburrOccasional ContributorI see that now I can define VBScript classes in similar fashion as I had been use to in VBScript for office. I do not think I could do this back in TextComplete 5 and so I used ODT. Still a good size conversion task but more doable than converting to JScript as I would prefer.
- mdhContributorHow about ODT support for 10.50, is that re-introduced ?
and can I use the ODT Plug in I used for 10.40 or do I need to ask Smartbear again for a new one for 10.50 ? - TanyaYatskovskaSmartBear Alumni (Retired)
Hi Morten,
The ODT feature wasn't reinstated in TestComplete 10.5. Our Product Management is aware of the fact that many of our customers are requesting to return this feature back. So, they consider this in future. However, right now, you will need to contact our Support team to get the ODT modules for TestComplete 10.5.
- rrivestContributor
Where I'm currently working, we managed to replicate OO design without using ODT in C#.
Here's a sample "Browser" class that allowes to instantiate either a standard browser or a virtual device for mobile web.
We simply expose the methods and properties as needed
function Fureteur()
{
// Initialisation des FIELDS
this.Component = Object(); // L'object TestComplete.Browser
this.BrowserType = varInteger; // Le type de Browser (int, -1=IE, -2=FF, -3=Chrome)
this.currentPage = Object(); // Object de type Page, Page courante du Browser
this.URL = varString; // URL de la page de type String
this.isVirtual = Boolean();
this.Description = varString;
this.Open = function(BrowserType,isVirtual) // Instancie un fureteur selon le type
{
if (isVirtual) { // Si on utilise un Browser Virtuel
this.isVirtual = true;
var supported = VirtualBrowsers.Count;
if (BrowserType<supported) // Est-ce que celui demandé est supporté (Voir dans les Projets-Properties-Open App-Web Test-Virtual
{
var mybrowser = VirtualBrowsers.Item(BrowserType);
Log.Message(mybrowser.UserAgent);
}
this.Description = mybrowser.UserAgent;
} else {
this.isVirtual = false;
switch (BrowserType) {
case "-1" : Log["Message"]("Fureteur demandé : " + "Microsoft Internet Explorer");
var mybrowser = Browsers["Item"](btIExplorer);
break;
case "-2" : Log["Message"]("Fureteur demandé : " + "Mozilla Firefox");
var mybrowser = Browsers["Item"](btFirefox);
break;
case "-3" : Log["Message"]("Fureteur demandé : " + "Google Chrome");
var mybrowser = Browsers["Item"](btChrome);
break;
default : Log["Message"]("Fureteur demandé : " + "Microsoft Internet Explorer");
var mybrowser = Browsers["Item"](btIExplorer);
}
Log.Message("Fureteur : "+ mybrowser.Description);
Log.Message("Family " + mybrowser.Family);
this.Description = mybrowser.Description;
}
mybrowser.Run(Project["Variables"]["StartupURL"]); // La variable StartupURL se retrouve dans tous les projets.
this.Component = Sys.Browser("*",0); // Pour obtenir le processus du Browser
this.BrowserType = BrowserType;
Log["PopLogFolder"]();
return this.Component; // On retourne l'objet Browser
}
this.Navigate = function(url) // Navigue vers l'URL jusqu'à chargement complet
{
Log["AppendFolder"]("Navigation vers l'URL : "+url);
var myClock = HISUtils["StopWatch"];
myClock["Start"]();
var myPage = this.Component["ToUrl"](url);
while (myPage["contentDocument"] == null)
Delay(200);
while (myPage["contentDocument"]["readyState"] != "complete")
myPage["Wait"]();
Project["Variables"]["CurrentPage"]=myPage;
myClock["Stop"]();
//var myName = MakeLogName(Project["Variables"]["MyProject"]);
//PushToLog(myName,"Page ; " + url + " ; "+myClock["ToString"]());
Log["Message"](url + " ; "+myClock["ToString"]());
this.currentPage = myPage;
this.URL = myPage["URL"];
Log["PopLogFolder"]();
return this.currentPage; // On retourne l'objet Page de la navigation
}
this.Close = function() // Ferme le fureteur
{
this.Component["Close"]();
return null;
}
}
To instantiate the Browser
myBrowser = new Fureteur
myBrowser["Open"](-1,false) for a standard browser
myBrowser["Open"](3,true) for one of the listed Virtual Devices in Project Properties
Related Content
- 2 years ago
- 5 years ago
Recent Discussions
- 22 hours ago
- 22 hours ago
- 5 days ago