Tips for WinRunner script development
-
Folder Structure
Folder Subfolder Description WR_Scripts Place where all WinRunner scripts are located Data MS Access database, GUI file for all tests, other data files which are necessary for running scripts Regression_Tests ABC_!Batch ABC_Test1… … ABC_Testn… Location of WR scripts, which are invoked for regression testing. ABC_!Batch invokes all other tests. Results Batch test result files (logs, screenshots etc.) ABC_Functions WR script where commonly used functions are stored. Tools Other tools which are developed for our needs (e.g. *.vbs scripts) -
To store scripts in CVS.
Following files and directories are necessary, to run scripts (assuming you don't use text or bitmap checks, locator tracks etc.):
Script_name — directory with name of the script header — script meta info script — actual script code db — directory crvx.asc — necessary system file (should care to store only initial content without runtime/debug info) tm.asc — another system file (initial content is the same as crvx.asc) exp — directory def — this file is necessary for CVS not to allow removing empty directory
- Script structure WR has two types of scripts — Main Test or Compiled Module. (You can see this property in File-test Properties...-test type.) Main test can be executed explicitly by Test-Run.., but compiled module can be invoked only from main test. Currently we have only one compiled module — ABC_Functions (it could be changed if/when we will have trouble with one too big file). It includes all common functions for ABC testing. All functional tests in Regression_Tests folder are main test scripts. Each functional script under Regression_Tests folder can be executed from ABC!_Batch file, or separately. If script is invoked separately, it generates unique names (where they are necessary) using current date and uses any data (usual first), which it can be got from referenced data. If script is invoked from other script (usually from ABC_!Batch), then it can receive input parameters by passed array which keys has necessary values. (Script parameter can be seen in File-Test Properties…-Parameters.)
- Batch Mode Public variable batch_mode in ABC_Functions script is used to control script initialization and logging. If this variable is ON, then ABC_Functions script is not reloaded for each script execution, and log is additionally done in MS access database (in table -Log) and in text files (stored in ../Results) folder. Set this variable OFF (usually, in script development and debugging), if you don't need logging in Access and log files, or don't need to reload ABC_Functions file for each functional test execution.
-
Standard Script Design.
We don't use File-Test Properties…-Run feature, because using this feature only script initialization can be handled, but not finalization. Therefore all script initialization/finalization is done by explicit function invocation:
if (batch_mode!=ON) reload("ABC_Functions",0,1); if (start() != E_OK) treturn (E_GENERAL_ERROR); #==== The END ==== finish(); treturn(E_OK);
- Script naming convention To group scripts by their domain, is following: ABC_ObjectOperation, e.g. ABC_ProductHeaderAdd, ABC_ProductSearch
-
GUI element name convention
To group similar elements together, windows and data elements (text, combo boxes) are named as DataElementName, e.g.
AssetID, AssetName
Other graphical objects have prefixes:
aName for links, tName for tables, bName for buttons
-
Return conventions for search
Most of search function and scripts are designed in such way, that they return:
E_OK or value on exact match value — similar value for partial match error code (E_ERROR, E_NOT_FOUND, etc) — if value was not found
- Automated script management procedures, which WR doesn’t support. Generate function list with generate_function_list.vbs script, test params are counted with generate_sript_list.vbs script. Change tl_step to my_tl_step and set_window to my_set_window functions using change_word.vbs script. In difference from WR, script will replace word in all tests for selected folder and will not replace word, if found string is already substring of destination string. This means, replacement will not make my_set_window to my_my_my_my_set_window after several replacements.
-
Navigation to the necessary windw.
To avoid refreshing and other problems and for centralized management, navigation bar is always selected by function navigate(). E.g:
navigate("bHome|bProducts|bManageAsset");
-
Check web browser window
If you need to check web browser window (browser_main_window in WR terms), do not use if(win_exists("window") == E_OK)is either slow (big timeout), or fails sometimes (if timeout is small). Faster and error prone solution is checking all "browser_main_window"s and their titles wit custom function get_mainwin_desc():
cwin=get_mainwin_desc("window title") # get necessary browser_main_window if (cwin!="") ...
-
Custom properties for GUI elements
Some objects WR recognizes as generic object. Adding custom properties for objects allows to manage them as concrete objects with by their properties:
# add new object html_table to GUI map Configuration set_class_map("html_table", "object"); # define custom html_table class as specialized object # define its mandatory, optional properties and identification method set_record_attr("html_table", "class html_name", "attached_text MSW_id MSW_class", "location"); set_record_method("html_table", RM_RECORD); # and recording method (context sensitive) # add attributes to html_radio_button object set_record_attr("html_radio_button", "class MSW_class html_name", "", "location");
-
Fill object description
Selecting link/image/etc by full description (element description can be got by Tools-GUI Spy…-Spy-Ctrl+F3-Copy). The following will work if you have a value set for the variable project_name. If not than this will not work.
web_link_click("{class: object,MSW_class: html_text_link,html_name: ""project_name""}");
- Set GUI element property dynamically If you need to set GUI element property dynamically, you can use full element description as mentioned above, but you can also store element in GUI file, and change only some property (usually location) dynamically. This allows shorter code, because you can use logical name in place full description: GUI_buf_set_desc_attr(gui_home, "Window", "Object", "location", row); # set location attribute for object in GUI file button_set("SelectSalesBatch",ON); # click on this object
-
Verify message in a dialogue box.
Use function get_message()
result=get_message(Msg,"OK"); my_tl_step("People Terminate",result,Msg);
-
Using wildcards in GUI file.
Wildcards are useful when modifying the physical description of a object in the GUI map. I’ve included the following example which is the physical description of the personSelect radio button on the Staffing page.
Examples:
{ class: radio_button, MSW_class: html_radio_button, html_name: "!personSelect.*", part_value: "!.*", }
{ class: radio_button, MSW_class: html_radio_button, html_name: "!personSelect.*", part_value: "!.*", location: 0; }
"Dialog Box": { class: window, label: "!.*", MSW_class: Dialog }
{ class: push_button label: "!O[a-zA-Z]*" }
-
Posting a pass or fail message to the Test Results log
When Batch test is running, if some of atomic tests is failed (exception handler has executed
eval("texit (E_GENERAL_ERROR);
), tl_step function stops logging future events. Therefore better is using my_tl_step() function. It will log in file. It always works. E.g.my_tl_step("People Terminate",result,Msg);
-
If you want to have different code for different browser use the following function to identify the browser type:
I don’t have the list of codes, but I know that 6=IE 5.5
version=GetWindowActiveBrowserVersion("spMain");
-
Samples for finding text in window.
Sometimes is necessary to check, that text doesn’t appear in window (i.e. to check delete), but function web_frame_text_exists generates an error, if text is not found, therefore text search functions are preferable:
# web_frame_get_text_count — the most preferable and the easiest way: if(web_frame_get_text_count(frame, text, occur)!=E_STR_NOT_FOUND) print "web_frame_get_text_count " text & " " & occur & " occurrences FOUND" ; else print "web_frame_get_text_count " & text & " NOT FOUND"; # web_frame_get_text — also works if(web_frame_get_text(frame, a,text, text, 1)!=E_NOT_FOUND) print "web_frame_get_text " text & " FOUND"; else print "web_frame_get_text " & text & " NOT FOUND"; # web_find_text very expensive (finds graphical position of text) web_find_text (frame, text, pos); if (pos[0]!="") print "web_find_text " & text & " FOUND at [" & pos[0] & "," & pos[1] & "," & pos[2] & "," & pos[3] & "]" ; else print "web_find_text " & text & " NOT FOUND";
-
Finding an object in list or table
There are two approaches to find an object in list or table – more generic with loop and counter, and more fast, but more dependent on GUI (table description). Use one of them where it is more approprate:
# generic algorithm, don't need table, but slower and harder to get necessary object if many similar exist i=0; while (obj_exists("{class: push_button,MSW_class: html_push_button,html_name: View,location: "i"}")==E_OK) i++; button_press("{class: push_button,MSW_class: html_push_button,html_name: View,location: "i-1"}"); # get child object from table cell, fast and simple but depends on table description tbl_get_rows_count("Table1",i); web_obj_get_child_item("Table1","#"i, "#2","html_push_button",0,button); button_press(button);
-
Get physical description of html table from name.
If table are generated automatically (ASP, JSP), then sometimes it is necessary to get the physical description of one and to operate on it instead of logical name, which must present in GUI mapping file.
set_window(window_name, 1); # sets active window for (i=1;i<99;i++) { # checks up to 99 tables $description = "{class: object,MSW_class: html_table,location: " & i & "}"; # Generate description of table ; if (obj_exists(description)==E_OK) { web_obj_get_info(description, "name", found_name); # check for name of found table if (found_name == expected_name) found_location = i; # This is the location, we are interested break; } else break; # Exit on first object non-existence } final_$description = "{class: object,MSW_class: html_table,location: " & found_location & "}"; ;
tbl_get_rows_count ("table_name",rows_count); for(i=first_row;i<=rows_count;i++) { tbl_get_cell_data("table_name", "#"&i,"#"&column, tmp_name); if (index(tmp_name,"text_to_find")!=0) break; # if found, break } web_obj_get_child_item("table_name", "#"&i,"#2", "html_text_link", 0, item); # Select link web_link_click(item); # and click
- Operating with Dialog Windows Usually it is hard to get Dialog Windows by they name, therefore use function get_win_desc().
-
To substitute non significant (leading) spaces with zeros for integer (e.g. print 00005 in place of 5) use:
sprintf("%0.5d",5);
-
obj_type doesn’t clear previous contents of Edit field. To do this use this statement just before your obj_type:
obj_type("
"," "); -
Dynamic HTML and Javascript
WR context sensitive functions don't work with dynamic tables, which are rendered on client side using javascript. Only analog functions can be used. e.g:
Add Row button and all text boxes should clicked only graphically, e.g.:
move_locator_abs(858, 428); mtype ("
- +"); move_locator_abs ( 600,488); mtype (" - +"); type (" asdasd"); move_locator_rel ( 140, 0); mtype (" - + - +"); type (" S "); move_locator_rel ( 320, 0); mtype (" - +"); type (" asdasd"); button_press("bSave");
-
Generation of unique strings:
Simple random generation:
a=rand()*10;
and for determined valuea=date_from_WRsecs("YYMMDDHHNNSS");
. Sometimes you need that generated string is in some range, but still is quite determined. Then use "hashing" of string using modulus function:a=chr(date_from_WRsecs("NN")%25+65)&chr(date_from_WRsecs("SS")%25+65);
-
Exceptions
We have tried several times to implement WR exceptions. They are 2 types:
GUI web exceptions (Tools-Web Exception Handling…)
Appears on several browser's warnings (e.g. security warnings, asking for passwords, "page can not be displayed" etc.)
TSL exceptions (Tools-Exception Handling). Appears as exception in code execution.
However, due to WR limitations we didn't rely on them. Usual exception handling is interrupting script if somewhere in medium stage it went totally wrong. WR can't close test from other test therefore proper handling isn't possible. Following example shows how sripts should be handled:
# Batch script reload("Tools",0,1); define_tsl_exception ("set_window_exc", "set_window_exc_handler", E_NOT_IN_MAPPING,"set_window"); exception_on ( "set_window_exc" ); call_close "tests1"(); call_close "tests2"(); call_close "tests3"(); # Atomic (functional) scripts Script1, Script2, Script3 a=set_window("taram"); print "test1 finished a:"a; # compiled module (Tools) script public function set_window_exc_handler(in rc, in func) { print func" failed with code "rc; eval("texit (E_GENERAL_ERROR);"); # we have to use such trick, because there is no proper method for this }
eval("texit (E_GENERAL_ERROR);");
is called from exception handling function, strip execution is stopping at all in ..libns_wr_funcs module (and closes WR). But, if we invoke simple exception_handler from custom function e.g. my_set_window, it is working nice. Probably, for other simpler cases, exception handling is working properly.
Tags
English
Testing
Created by Administrator on 2008-11-23 16:54
Last modified by Administrator on 2021-04-13 14:29
Created by Administrator on 2008-11-23 16:54
Last modified by Administrator on 2021-04-13 14:29