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
    To clear directory structure before commiting, use delete_files.vbs script
  • 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);
    See !TEMPLATE script for more details.
  • 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: "!.*", }
    You can use this for selecting any name from the Search Results page rather than specifying the name. This will definitely work if you have only one person listed on the page. If you have more than one, you can add a location after the part value line, specifying which you want to select. 0 is the first, 1 the second, etc.

    { class: radio_button, MSW_class: html_radio_button, html_name: "!personSelect.*", part_value: "!.*", location: 0; }
    Examples (in gui mapping file)

    "Dialog Box": { class: window, label: "!.*", MSW_class: Dialog }
    This defines push buttons with labels Starting with "O" and then any count and type of letters
    { 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:
    version=GetWindowActiveBrowserVersion("spMain");
    I don’t have the list of codes, but I know that 6=IE 5.5
  • 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 & "}"; ;

    Find text in table column ad activate link or other item in necessary row:
    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("","");
    This emulates Home, Shift+End and Delete key presses, what in turn deletes contents of edit field.
  • 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");

    Save button can be clicked in context sensitive mode, e.g.:
    button_press("bSave");
  • Generation of unique strings: Simple random generation: a=rand()*10; and for determined value a=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 }
    If 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.
Created by Administrator on 2008-11-23 18:54
Last modified by Administrator on 2016-04-01 12:19
 
Xwiki Powered
Creative Commons Attribution 3.0 Unported License