Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
bernd_honsa
Explorer

Introduction




SAP FIORI is the new standard UI for all SAP applications. Its central management capability is the FIORI Dashboard.

The scripting of the SAP FIORI Dashboard is the new challenge for performance testing with HP LoadRunner because of two reasons.

The first reason is that a lot of user activities in the web browser are not relevant for the typical http(s) scripting because it is Javascript. Therefore, these actions will not be recorded by the HP VuGen. So the performance tester has to consider this fact and think more in the way of grey box testing. Thus a deeper understanding of the Application Under Test and of what is happening when the user executes a special Javascript action is needed.

The second reason is that the elements in the http(s) requests are highly dynamic. From my experience the general approach of “recording and automatic correlation” will not work 100% properly anymore. So the performance tester should more analyze the requests and related responses to find out which dynamic IDs exist and when they were generated (e.g. by Javascript functions).

Based on the prior findings of my colleagues Sabine Nettky and Uwe Uhlemann I created a performance test script with HP LoadRunner for actions on the FIORI Dashboard. My following notes are intended to help a performance tester to fix possible errors in the LoadRunner scripts.

I used the “SAP Web” protocol. I tried the “TrueClient for Firefox” protocol and it worked for the complete Javascript activities. This is the great benefit of the “TrueClient” protocol, but I could not fix several authorization errors (e.g. HTTP 403) with it.
“SAP Web” has the disadvantage that you cannot record the Javascript actions. However, his is only a minor trade off when testing the performance of a backend system (ERP, Portal etc.). Please find more details below regarding this issue.

In my customer project the system landscape consisted of a load balancer, a SAP Enterprise Portal and a Backend System (ERP). Https was used as the communication protocol with the portal.
I will describe the various correlation rules using individual code fragments.

For the scripting I used HP LoadRunner 12.01.


Recording/Replay Parameter


Recording Parameter


I used the following recording options so that it worked without errors:








Illustration 1: Recording Options- HTTP Properties

In the HTTP Properties -> Headers options I had to add the following entries for [Record headers in list]:

  • Csrf-Token

  • X-CSRF-Token

  • x-csrf-token

  • x-Token

  • X-UI5-Component


Also in the HTTP Properties in the Recording schemes I had to add the marked three entries:



Illustration 2:Recording Options – HTTP Properties

For the Network options I recommend to use both – Socket and WinNet. Please do not  only use Socket level due to recording issues for the FIORI dashboard.



Illustration 3: Recording Options - Network

Replay Parameter


Really important for the replay are the following parameters.

The first parameter belongs to the user session in the emulated browser. Here I recommend to set the marked parameter in case you run with login/logout in the action part of the script.




Illustration 4: Replay Options - Browser Emulation

For the proxy parameter I recommend the option “No proxy” if possible.

For the preferences of the Internet Protocol in the parameters section you shouldn’t forget to activate “JavaScript”.




Illustration 5: Replay Options - Internet Protocol Preferences

Authorization


The first manual changes in the script are related to the Transport Layer Security (TLS). If you don’t add the requests to your script then you will get HTTP 401/403 errors in the replay. Put these requests prior to your very first requests to the application.

web_set_user("{userid}",

             "{password}",

             "{server-address}:{server-port}");

// manually changes for Warning -27776

web_set_sockets_option("SSL_VERSION", "TLS");

web_set_certificate_ex(

            "CertFilePath=certificate.pem",

            "CertFormat=PEM",

            "KeyFilePath=certificate.pem",

            "KeyFormat=PEM",

            "CertIndex=1",

//          "Password={certificate-password}",

            LAST );


The certificate file (certificate.pem) was created with the HP LoadRunner tool “openssl”, but you can also follow the approaches from websites like





If you build your script in a way that login and logout are part of the Action then you have to ensure that the “web_set_certificate_ex” request will be sent only once for the first iteration.

Otherwise you will get an error message in the second iteration.

XSRF Token


The XSRF Token is needed for all requests that change something on the FIORI Dashboard (e.g. add new group, change tiles in a group). Without the correct token your script will run without any HTTP error, but nothing will be changed in the Dashboard.

web_reg_save_param_regexp(

      "ParamName=XSRF-Token",

      "RegExp=var FLPxsrfTokenValue = \'(.*)\'",

      SEARCH_FILTERS,

      "Scope=Body",

      "RequestUrl=*/portal/fiori*",

      LAST);

web_custom_request("fiori",

      "URL=https:// {server-address}:{server-port}/irj/portal/fiori",

      "Method=GET",

      "Resource=0",

      "RecContentType=text/html",

"Referer=",

"Snapshot=t312.inf",

"Mode=HTTP",

LAST);

X CSRF Token


In some cases a X-CSRF Token is needed. In my customer situation it was actually not needed, however if you have a Cross-Site Request Forgery protection for your SAP Gateway infrastructure then you have to add this function prior to the web request. You should record several web requests and try to find the value.

Once found you should correlate it and reuse it in the response. You should create an “anti-XSRF cookie” and name it like “sap-XSRF_<SID>_<client>”.

web_add_header("X-CSRF-Token", "fetch");

An example for a correlation is here:

web_reg_save_param_ex(

"ParamName=XCsrfToken",

"LB/IC=x-csrf-token: ",

"RB/IC=\r\n",

SEARCH_FILTERS,

"Scope=Headers",

LAST);

For all modifying requests you have to include this newly created token in the HTTP request header field with the same name “X-CSRF-Token”. Otherwise you would get a HTTP 403 error.

Here is an example (from another customer case) for the usage of the token:

web_custom_request("batch_1",

"URL=http://{server-address}/sap/opu/odata/sap/RETAILSTORE_TRF_STOCK_SRV_01/$batch",

"Method=POST",

"Resource=0",

"RecContentType=multipart/mixed",

"Referer=http:// ://{server-address}/sap/bc/ui5_ui5/ui2/ushell/shells/abap/FioriLaunchpad.html",

"Snapshot=t22.inf",

"Mode=HTTP",

"EncType=multipart/mixed;boundary=batch_7daa-5efa-62be",

"Body=--batch_7daa-5efa-62be\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nGET TransferTypes HTTP/1.1\r\nAccept-Language: en\r\nAccept: application/json\r\nMaxDataServiceVersion: 2.0\r\nDataServiceVersion: 2.0\r\nx-csrf-token: {XCsrfToken}==\r\n\r\n\r\n--batch_7daa-5efa-62be--",

LAST);

TILE ID


In case you want to interact with the FIORI Dashboard (e.g. add tile to a group) then you need the Tile ID for each tile on its group. This Tile ID has a fix (stable) segment and a dynamic segment. Therefore you have to correlate this value from the first response of the Portal Launcher (com.sap.portal.navigation.portallauncher.default).

// Manually change for catch the dynamic tile ID

web_reg_save_param_regexp("ParamName=Tile-ID-General",   

"RegExp=\"id\":\"General\",\"title\":\"General\",\"isPreset\":true,\"tiles\":\\[{\"id\":\"(.*?)\",\"title",

            SEARCH_FILTERS,

            "Scope=Body",

            "RequestUrl=*portallauncher.default*",

            LAST);

The value looks like “7d6ace3e5d6e2cd9efe1caadcfc5993e_UUID0c43fa0f_d741_4c172413”.

For subsequent actions on the FIORI dashboard you need the first segment of this Tile ID for the other groups. Therefore I copied this first segment in an additional parameter “TileID1Segment”. The initialization of “TileID1Segment” looks like this (based in the global.h):

Char TileID[35]; // 32 characters for the ID + 3 characters for string end

These are the commands after saving the parameter from the web response:

// Save the first segment of the Tile-ID in the parameter "TileID1Segment"
// This value is needed for the requests to change anything of the dashboard
strncpy(TileID,lr_eval_string("{Tile-ID-General}"),32);
lr_save_string(TileID, "TileID1Segment");

Modifying actions on the FIORI Dashboard


For the different actions on the FIORI Dashboard you will need the already mentioned “Tile-ID”, “TileID1Segment”” and the “XSRF Token”. A basic web request looks like this (e.g. for adding an existing tile to the group “Home”):

web_custom_request("com.sap.portal.navigation.portallauncher.default",

"URL=https://{server-address}/irj/servlet/prt/portal/prtpos/com!252esap!252eportal!252enavigation!252eportallauncher!252edefault.pcd!253aportal!255fcontent!252fevery!255fuser!252fgeneral!252fdefaultFLPFrameworkContent!252fFLPDesktop!252fframeworkPages!252fFLPFrameworkPage.com!252esap!252eportal!252eFioriLaunchpadPlUtilitiesComponent!7bSetPersonalizationData!7d--/prttarget/pcd!253aportal_content!252fevery_user!252fgeneral!252fdefaultFLPFrameworkContent!252fFLPDesktop!252fframeworkPages!252fFLPFrameworkPage.com!252esap!252eportal!252eFioriLaunchpadPlUtilitiesComponent/prtroot/com.sap.portal.navigation.portallauncher.default",

"Method=POST",

"Resource=0",

"RecContentType=text/html",

"Referer=https://{server-address}/irj/portal/fiori",

"Snapshot=t321.inf",

"Mode=HTTP",

"EncType=application/x-www-form-urlencoded; charset=UTF-8",

"Body=flp_pl_data=%7B%22persistencyStructureCompatibleWith%22%3A%22SP15%22%2C%22groups%22%3A%5B%7B%22id%22%3A%22group_0%22%2C%22title%22%3A%22Home%22%2C%22isPreset%22%3Atrue%2C%22tiles%22%3A%5B%7B%22id%22%3A%22{TileID1Segment}_UUIDc360d38a_64cf_0ee2a375%22%2C%22title%22%3A%22Tile-Name%22%2C%22size%22%3A%221x1%22%2C%22tileType%22%3A%22sap.ushell.tiles.ep.EPImageTile%22%2C%22properties%22%3A%7B%22title%22%3A%22Tile-Name%22%2C%22permanentTile%22%3Atrue%2C%22imageType%22%3A%22Image+ (Full-Width) %22%2C%22imageSource%22%3A%22%2Fcom.sap.portal.resourcerepository%2Frepo%2FFLPRESOURCES%2FTile-Name_dynamic.png%22%2C%22recommendedUntil%22%3Anull%2C%22isNew%22%3Afalse%2C%22info%22%3Anull%2C%22subtitle%22%3Anull%2C%22keywords%22%3A%5B%5D%2C%22targetURL%22%3A%22%23LEGACY--1837212095-Tile-Name%22%2C%22semanticObjStr%22%3A%22%23LEGACY--1837212095-Tile-Name%22%7D%2C%22isEPDynamic%22%3Afalse%7D%5D%2C%22order%22%3A0%7D%2C%7B%22id%22%3A%22General%22%2C%22title%22%3A%22General%22%2C%22isPreset%22%3Atrue%2C%22tiles%22%3A%5B%7B%22id%22%3A%22{Tile-ID-General}%22%2C%22title%22%3A%22Tile-Name%22%2C%22size%22%3A%221x1%22%2C%22tileType%22%3A%22sap.ushell.tiles.ep.EPImageTile%22%2C%22properties%22%3A%7B%22title%22%3A%22Tile-Name%22%2C%22permanentTile%22%3Atrue%2C%22imageType%22%3A%22Image+(Full-Width%22%2C%22imageSource%22%3A%22%2Fcom.sap.portal.resourcerepository%2Frepo%2FFLPRESOURCES%2FTile-Name_dynamic.png%22%2C%22recommendedUntil%22%3Anull%2C%22isNew%22%3Afalse%2C%22info%22%3Anull%2C%22subtitle%22%3Anull%2C%22keywords%22%3A%5B%5D%2C%22targetURL%22%3A%22%23LEGACY--1837212095-Tile-Name%22%2C%22semanticObjStr%22%3A%22%23LEGACY--1837212095-Tile-Name%22%7D%2C%22isEPDynamic%22%3Afalse%7D%5D%2C%22order%22%3A1.7976931348623157e%2B308%7D%5D%7D&xsrfid={XSRF-Token}",

LAST);

The important value that determines the actual action on the Dashboard is parameter “flp_pl_data” in the request body.

For a better understanding it is possible to translate all special characters beginning with “%” from URL format in readable format.

I used for that the character table (https://en.wikipedia.org/wiki/ASCII#ASCII_printable_code_chart ).

{“persistencyStructureCompatibleWith”:”SP15”,”groups”:

[

{“id”:”group_0”,
”title”:”Home”,
”isPreset”:true,
”tiles”:


[

{“id”:”{TileID1Segment}_UUIDc360d38a_64cf_0ee2a375”,
”title”:”Tile-Name”,
”size”:”1x1”,
”tileType”:”sap.ushell.tiles.ep.EPImageTile”,
”properties”:


{“title”:”Tile-Name”,
”permanentTile”:true,
”imageType”:”Image+ (Full-Width) “,
”imageSource”:”/com.sap.portal.resourcerepository/repo/FLPRESOURCES/Tile-Name_dynamic.png”,
”recommendedUntil”:null,
”isNew”:false,
”info”:null,
”subtitle”:null,
”keywords”:[],
”targetURL”:”#LEGACY--1837212095-Tile-Name”,
”semanticObjStr”:”#LEGACY--1837212095-Tile-Name


},
”isEPDynamic”:false


}

],

”order”:0},

{“id”:”General”,
”title”:”General”,
”isPreset”:true,”tiles”:


[

{“id”:”{Tile-ID-General}”,
”title”:”Tile-Name”,
”size”:”1x1”,
”tileType”:”sap.ushell.tiles.ep.EPImageTile”,
”properties”:


{“title”:”Tile-Name”,
”permanentTile”:true,
”imageType”:”Image+(Full-Width”,
”imageSource”:”/com.sap.portal.resourcerepository/repo/FLPRESOURCES/Tile-Name_dynamic.png”,
”recommendedUntil”:null,
”isNew”:false,
”info”:null,
”subtitle”:null,
”keywords”:[],
”targetURL”:”#LEGACY--1837212095-Tile-Name”,
”semanticObjStr”:”#LEGACY--1837212095-Tile-Name


},

”isEPDynamic”:false

}

],

”order”:1.7976931348623157e%2B308

}

]

}

The green parameter string in the example above is responsible for adding tile “Tile-Name” to the group “Home”. The precondition is that the group “Home” was empty, respectively this group hasn’t any tile assigned yet prior to this web request.

So you can also delete a tile from a group by sending an empty bracket “[]” like this:

{“persistencyStructureCompatibleWith”:”SP15”,”groups”:

[

{“id”:”group_0”,
”title”:”Home”,
”isPreset”:true,
”tiles”:


[],

”order”:0},

{“id”:”General”,
”title”:”General”,
”isPreset”:true,”tiles”:


[

{“id”:” {Tile-ID-General}”,
”title”:”Tile-Name”,
”size”:”1x1”,
”tileType”:”sap.ushell.tiles.ep.EPImageTile”,
”properties”:


{“title”:”Tile-Name”,
”permanentTile”:true,
”imageType”:”Image+(Full-Width”,
”imageSource”:”/com.sap.portal.resourcerepository/repo/FLPRESOURCES/Tile-Name_dynamic.png”,
”recommendedUntil”:null,
”isNew”:false,
”info”:null,
”subtitle”:null,
”keywords”:[],
”targetURL”:”#LEGACY--1837212095-Tile-Name”,
”semanticObjStr”:”#LEGACY--1837212095-Tile-Name


},

”isEPDynamic”:false

}

],

”order”:1.7976931348623157e%2B308

}

]

}

Checks on the FIORI Dashboard


In case you want to check that special elements e.g. tiles are visible or that a page was fully and correctly loaded then you may insert some web checks.

This request validates that a special tile is loaded on the dashboard, e.g. after adding it to a group:

// check if the Tile is on the dashboard

web_reg_find("Text={\"title\":\"Tile-Name\",\"permanentTile\":true,",
LAST);


This request checks that an image was loaded with the page, e.g. as part of the “home” link.

// check if logo was loaded sapCompanyLogo.png

web_image_check("Company_Logo", "Alt=Logo", LAST );

With this request you are able to validate that the login for the given user ID was successful, because the user ID should be included in the response.

// Search for a string like 'SAPUSER.CORP_LDAP.{userid}'}'

web_reg_find("TextPfx=SAPUSER.CORP_LDAP.","TextSfx='}",
LAST);


In case you want to validate that an added tile is in the group, then you may use the following request.

// Manually Change - Validate that My Home has a Tile

// "id":"group_0","title":"Home","isPreset":true,"tiles":[{"id":"

//       {TileID1Segment}_UUID0c43fa0f_d741_4c172413","title":"Tile-Name"

web_reg_find("TextPfx=\"id\":\"group_0\",\"title\":\"Home\",\"isPreset\":
true,\"tiles\":[{\"id\":\"{TileID1Segment}",
"TextSfx=\",\"title\":\"Tile-Name\"",
LAST);