This sample app just shows how to execute JSF requests through Gatling stress tool against Primefaces showcase.

Running it

mvn gatling:execute
To chose a specific simulation use -D flag, example:
-Dgatling.simulationClass=com.rmpestano.gatling.perf.DatatableSelectionSimulation
otherwise all simulations will run.

JSF Viewstate

When working with JSF you need to handle the ViewState which basically stores JSF component tree state and must be preserved across requests.

The way to extract information from http requests with Gatling is using http checks, here is how its done for JSF ViewState:

  val jsfViewStateCheck = css("input[name='javax.faces.ViewState']", "value")
    .saveAs("viewState") (1)

  val jsfPartialViewStateCheck = xpath("//update[contains(@id,'ViewState')]")
    .saveAs("viewState") (1)
1 Saves the viewState in Gatling session.

These checks needs to be called on each request so we can preserve the viewState. An idea is to put them in template requests like below:

def jsfGet(name: String, url: Expression[String]) =
    http(name)
      .get(url)
      .check(jsfViewStateCheck)

def jsfPost(name: String, url: Expression[String]) = http(name)
    .post(url)
    .formParam("javax.faces.ViewState", "${viewState}")
    .check(jsfViewStateCheck)

def jsfPartialPost(name: String, url: Expression[String]) = http(name)
    .post(url)
    .header("X-Requested-With", "XMLHttpRequest") (1)
    .header("Faces-Request", "partial/ajax") (2)
    .formParam("javax.faces.partial.ajax", "true") (2)
    .formParam("javax.faces.ViewState", "${viewState}") (3)
    .check(jsfPartialViewStateCheck)
1 http ajax call format
2 JSF ajax call indication, JSF will return a xml in the response
3 already stored (in gatling session) view state

So we basically have two kinds of request, a normal and a partial http request. Partial requests are mainly to deal with ajax calls (e.g p:ajax or f:ajax).

As you can see, in post requests the viewState already must be present in gatling session.

CommandButton ajax

Here is an example of JSF ajax button call:

 <p:commandButton value="Ajax Submit" id="ajax" update="growl" actionListener="#{buttonView.buttonAction}" styleClass="ui-priority-primary" />
This code is taken from Primefaces showcase: http://www.primefaces.org/showcase/ui/button/commandButton.xhtml

And here is the correspondent Gatling call:

def ajaxButtonCall = jsfPartialPost("request_ajax_button", "/ui/button/commandButton.xhtml")
    .formParam("javax.faces.source", "${btAjax}")
    .formParam("javax.faces.partial.execute", "@all")
    .formParam("javax.faces.partial.render","${growlId}")
    .formParam("${btAjax}", "${btAjax}")
    .formParam("${form}", "${form}")
    .check(status.is(200),growlResponseCheck)

To find which params you need to pass in the request, firebug is your best friend:

Furebug

The initial call

As stated before we need a previous JSF call to extract and store variables in Gatling session like button id in ${btAjax} variable, form id in ${form} and so on.

Here is the initial call:

 jsfGet("saveState", "/ui/button/commandButton.xhtml")
        .check(status.is(200), ajaxButtonIdCheck, formIdCheck,growlIdCheck)

and then the checks that stores the component’s id needed in the request:

val ajaxButtonIdCheck = css("button[id$='ajax']", "id")
    .saveAs("btAjax")

val formIdCheck = css("form", "id")
    .saveAs("form")

val growlIdCheck = css("span[id$='growl']", "id")
    .saveAs("growlId")
css selector $= denotes to 'endsWith' and is very helpful when working with JSF prepended id strategy.

Check the response

As you’ve probably noticed, there is a growlResponseCheck after the request completes to assure that it has performed as the real application.

The response contains the growl component, use firebug tab response to see its format:

<partial-response id="j_id1"><changes><update id="j_idt87:growl"><![CDATA[<span id="j_idt87:growl" class="ui-growl-pl"
data-widget="widget_j_idt87_growl" data-summary="data-summary" data-severity="all,error" data-redisplay="true"></span>
<script id="j_idt87:growl_s" type="text/javascript">$(function(){PrimeFaces.cw('Growl','widget_j_idt87_growl',{id:'j_idt87:growl',
sticky:false,life:2000,escape:true,msgs:[{summary:"Welcome to Primefaces!!",detail:"",severity:'info'}]});});</script>]]></update>
<update id="j_id1:javax.faces.ViewState:0"><![CDATA[3557342521411359398:379028951273673786]]></update></changes></partial-response>

There are multiple ways to check that response, I’ll use xpath this time:

  val growlResponseCheck = xpath("//*[contains(text(),'Welcome to Primefaces!!')]")

If you use wrong selector the request will fail, example:

xpath("//*[contains(text(),'Welcome to Richfaces!!')]")

will fail with:

---- Response Time Distribution ------------------------------------------------
> t < 800 ms                                            34 ( 61%)
> 800 ms < t < 1200 ms                                   9 ( 16%)
> t > 1200 ms                                           12 ( 21%)
> failed                                                 1 (  2%)
---- Errors --------------------------------------------------------------------
> xpath(//*[contains(text(),'Welcome to Richfaces!!')]).find(0).exists      1 (100,0%)
, found nothing
===============================================================================

Complete CommmandButton simulation can be found here.

Ajax Behaviour event

For ajax behaviour event the approach is quite similar, here goes an example of keyup event on page http://www.primefaces.org/showcase/ui/ajax/event.xhtml:

  val formIdCheck = css("form", "id")
    .saveAs("form")

  val inputIdCheck = css("input[id$='firstname']", "id")
    .saveAs("inputId") //stores in gatling session id of input which id endswith 'firstname'

  val outputIdCheck = css("span[id$='out1']", "id")
    .saveAs("outputId")

  //checks the partial response xml result
  val outputValueCheck = xpath("//*[contains(@id,'out1') and contains(text(),'Gatling, JSF and Primefaces rules')]")


  def ajaxEventRequest = jsfPartialPost("request_ajax_event", "/ui/ajax/event.xhtml")
      .formParam("javax.faces.source", "${inputId}")
      .formParam("javax.faces.partial.execute", "${inputId}")
      .formParam("javax.faces.behavior.event", "keyup")
      .formParam("javax.faces.partial.event", "keyup")
      .formParam("javax.faces.partial.render","${outputId}")
      .formParam("${inputId}", "Gatling, JSF and Primefaces rules")
      .formParam("${form}", "${form}")
      .check(status.is(200), outputValueCheck)

  val AjaxEventScenario = scenario("ajaxEvent scenario")
      .exec(
        jsfGet("initialRequest", "/ui/ajax/event.xhtml")
          .check(status.is(200), inputIdCheck,outputIdCheck, formIdCheck)
      )
      .pause(2)
      .exec(ajaxEventRequest)
      .pause(1)

here is Gatling response to confirm the partial event is fired:

<partial-response id="j_id1"><changes><update id="j_idt87:out1"><![CDATA[<span id="j_idt87:out1">Gatling, JSF and Primefaces rules</span>]]></update><update id="j_id1:javax.faces.ViewState:0"><![CDATA[5642006804874081440:6246997700145170162]]></update></changes></partial-response>
For debugging purposes just enable request and response logs in test/resources/logback.xml file:
<logger name="io.gatling.http" level="TRACE" />

Complete ajaxEvent simulation can be found here.

Datatable row select event

Here is an example of row select event from this showcase page (third table).

First thing to do is to save row id (in the initial request) because its needed for row select event:

 val tableRowCheck = css("tbody[id='form:eventsDT_data'] > tr[role='row'] > td[role='gridcell']")
    .saveAs("rowId")

now we are ready to fire the event:

def datatableSelectCarRowEvent = jsfPartialPost("request_datatable_select_car", "/ui/data/datatable/selection.xhtml")
    .formParam("javax.faces.source", "form:eventsDT")
    .formParam("javax.faces.partial.execute", "form:eventsDT")
    .formParam("javax.faces.partial.render", "form:msgs")
    .formParam("form", "form")
    .formParam("form:eventsDT_instantSelectedRowKey","${rowId}")
    .formParam("javax.faces.behavior.event","rowSelect")
    .formParam("javax.faces.partial.event","rowSelect")
    .check(status.is(200), growlCheck)

growlCheck verifies the partial response which will be something like below:

<partial-response id="j_id1"><changes><update id="form:msgs">
<![CDATA[<span id="form:msgs" class="ui-growl-pl" data-widget="widget_form_msgs" data-summary="data-summary" data-detail="data-detail"
data-severity="all,error" data-redisplay="true"></span><script id="form:msgs_s" type="text/javascript">
$(function(){PrimeFaces.cw('Growl','widget_form_msgs',{id:'form:msgs',sticky:false,life:6000,escape:true,msgs:
[{summary:"Car Selected",detail:"77698f6d",severity:'info'}]});});</script>]]></update>
<update id="j_id1:javax.faces.ViewState:0"><![CDATA[-5013885715335809736:669939156470551654]]></update></changes></partial-response>

and here is the check:

Partial response check
 val growlCheck = xpath("//*[contains(text(),'Car Selected')]")
  .saveAs("growlValue") //save is just to confirm in printSession

Check complete datatable simulation here.

Test Result

the output should be something like this:

================================================================================
---- Global Information --------------------------------------------------------
> request count                                        110 (OK=110    KO=0     )
> min response time                                    231 (OK=231    KO=-     )
> max response time                                   1670 (OK=1670   KO=-     )
> mean response time                                   383 (OK=383    KO=-     )
> std deviation                                        287 (OK=287    KO=-     )
> response time 50th percentile                        244 (OK=244    KO=-     )
> response time 75th percentile                        266 (OK=266    KO=-     )
> mean requests/sec                                 13.019 (OK=13.019 KO=-     )
---- Response Time Distribution ------------------------------------------------
> t < 800 ms                                           101 ( 92%)
> 800 ms < t < 1200 ms                                   5 (  5%)
> t > 1200 ms                                            4 (  4%)
> failed                                                 0 (  0%)
================================================================================

Reports generated in 0s.
Please open the following file: /home/pestano/projects/gatling-jsf-demo/target/gatling/results/ajaxeventsimulation-1431742082396/index.html
Global: percentage of successful requests is greater than 99 : true
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 15.516s
[INFO] Finished at: Fri May 15 23:08:12 BRT 2015
[INFO] Final Memory: 7M/150M
[INFO] ------------------------------------------------------------------------

Also some detailed reports about the simulation are generated at target/gatling folder:

Gatling report

see full generated reports here: