7 Testing Web-Services

The last kind of JTR-runner is meant for testing web-services. These runners are called WS-enabled runners.


JTR 4.0 introduces a new web-services engine that leverages on the powerful JAX-WS 2.1.1 reference implementation. The former implementation based on Jakarta WSIF must now be considered obsolete and will soon be removed from JTR. This section will guide you through the steps required for writing WS-enabled runners, exploring one more example shipped with the jtrTestBenchClient sample JTR test-suite.


The WsArithmeticalTest runner


package jtr.test.jee.client;


import javax.xml.bind.JAXBElement;

import jtr.runners.AbstractWsRunner;

import jtr.test.IOutcome;

import jtr.test.jee.arithmetical.client.Sum;

import jtr.test.jee.arithmetical.client.SumResponse;


/**

*

* @author frusso

* @version

* @since

*/

public class WsArithmeticalTest extends AbstractWsRunner {

   

    /** Creates a new instance of WsArithmeticalTest */

    public WsArithmeticalTest() {

    }


    public void test() throws Throwable {

        userMsg = "Using WS-binding "+getBinding().getUniqueName();

        jtr.test.jee.arithmetical.client.ObjectFactory sof =

                new jtr.test.jee.arithmetical.client.ObjectFactory();

        Sum sumReq = sof.createSum();

        sumReq.setX(x);

        sumReq.setY(y);

        userMsg = userMsg + "\nSumming "+x+" and "+y;

        JAXBElement<Sum> req = sof.createSum(sumReq);

        JAXBElement<SumResponse> rsp = (JAXBElement) invoke(getBinding(),req);

        System.out.println("WS Sum is "+rsp.getValue().getReturn());

        userMsg = userMsg + "\nResult is "+rsp.getValue().getReturn();

    }


    public void receiveFailureNotification(Throwable t, String msg) {

    }


    public void cleanupResources() {

    }


    public void enrichOutcome(IOutcome outcome) {

        outcome.setUserObject(userMsg);

    }

   

    private String userMsg;

    private int x;

    private int y;

}


Extending the jtr.runners.AbstractWsRunner

Once again, one of the most important things to highlight is that in order to write a WS-enabled runner we have to inherit from the jtr.runners.AbstractWsRunner abstract class. As we can see we still have to provide our implementations of the all the methods discussed here.


The jtr.runners.AbstractWsRunner class provides the following facilities and information:

  1. method to perform synchronous request-response web-services invocations

  2. method to perform synchronous one-way web-services invocations

  3. methods to perform asynchronous web-services invocations

  4. accessor to the current WS-binding associated with the runner instance


  5. Here are the most relevant methods meant for performing web-services invocations, exposed by the AbstractWsRunner and by the jtr.runners.IRunnerWs interface:


  6.        /**

  7.      * Performs a synchronous invocation.

  8.      *

  9.      * @param binding The actual binding describing the web-services to invoke

  10.      * @param input The message

  11.      * @return The response

  12.      * @throws jtr.ws.WsProviderException

  13.      */

  14.     public Object invoke(Binding binding, Object input) throws WsProviderException;

  15.    

  16.     /**

  17.      * Performs a one-way webservice invocation.

  18.      *

  19.      * @param binding The actual binding describing the web-services to invoke

  20.      * @param input The message

  21.      * @throws jtr.ws.WsProviderException

  22.      */

  23.     public void invokeOneWay(Binding binding, Object input) throws WsProviderException;

  24.    

  25.     /**

  26.      * Performs an asynchronous invocation.

  27.      *

  28.      * @param binding The actual binding describing the web-service to invoke

  29.      * @param input The message

  30.      * @param rl The response listener to be notified when a response becomes available

  31.      * @return A reference to the <code>Future</code> instance representin the pending task

  32.      * @throws jtr.ws.WsProviderException

  33.      */

  34.     public Future<?> invokeAsync(Binding binding, Object input, IWsResponseListener rl)

  35.                      throws WsProviderException;

  36.    

  37.     /**

  38.      * Performs an asynchronous invocation.

  39.      *

  40.      * @param binding The actual binding describing the web-service to invoke

  41.      * @param input The message

  42.      * @return A reference to the response one should poll on

  43.      * @throws jtr.ws.WsProviderException

  44.      */

  45.     public IWsResponse invokeAsync(Binding binding, Object input) throws WsProviderException;


  46. Web-Services invocations inputs & outputs

  47. Input and output messages for web-services invocations are instances of classes generated by means of the xjc JAXB binding compiler, starting from a WSDL file that describes the web-service(s) we want to invoke. For example, the following instruction:


  48. xjc.sh -wsdl http://localhost:8080/jtrTestBench-jtrTestBench-ejb/Arithmetical?wsdl


  49. produces an output like this:


  50. parsing a schema...

  51. compiling a schema...

  52. jtr/test/jee/ObjectFactory.java

  53. jtr/test/jee/Sum.java

  54. jtr/test/jee/SumResponse.java

  55. jtr/test/jee/package-info.java


  56. This means that all the artifacts required to marshall and unmarshall both request and response messages have been successfully generated and we can use them. If you now go back to the runner code above, you can see that these classes are actually used to prepare the input message and to represent the received response.


  57. This digression on web-services input and output object classes merely serves to highlight that you can still leverage on the full JAX-WS stack, that encloses JAXB as well, for representing input and output messages. If you need further information about either JAX-WS or JAXB, please refer to their official web-pages.


  58. Preparing the jtr.xml file

  59. In order to have the above runner working, we need to define it into the out jtr.xml configuration file. Follows an example of how it should look like:


  60. <?xml version = '1.0' encoding = 'UTF-8'?>


  61. <!-- JTRunner is free software; you can redistribute it and/or modify

  62.     it under the terms of the GNU General Public License as published by

  63.     the Free Software Foundation; either version 2, or (at your option)

  64.     any later version.

  65. -->


  66. <test xmlns="http://jtrunner.sourceforge.net"

  67.       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  68.       xsi:schemaLocation="http://jtrunner.sourceforge.net file:./config/jtr.xsd"

  69.       runs="10">


  70.     <!-- JTR components factories -->

  71.     <factories>

        <factory key="IAssignmentPolicyFactory"

                 fqn="jtr.assigner.impl.DefaultAssignmentPolicyFactory" />

        <factory key="IOutcomeFactory" fqn="jtr.test.impl.DefaultOutcomeFactory" />

        <factory key="IWsHelperFactory" fqn="jtr.ws.jaxws.JaxWsHelperFactory" />

        <factory key="ITestCompletionListener"

                 fqn="jtr.test.impl.DefaultTestCompletionListener" />

        <factory key="ITestResultDisplayer" fqn="jtr.test.impl.DefaultTestResultDisplayer" />

        <factory key="ITestResultsExporter" fqn="jtr.test.results.exporters.impl.ExcelExporter" />

        <factory key="ITestScriptingEngine" fqn="jtr.script.impl.BshScriptEngine" />

        <factory key="ITestStatFunctions" fqn="jtr.lang.functions.impl.DefaultStatFunctionsFactory" />

    </factories>


  1.     <runner runs="2" binding="jboss-sum-soap-1.2">

  2.         <runner-fqn>jtr.test.jee.client.WsArithmeticalTest</runner-fqn>

  3.         <instance-count>2</instance-count>

  4.         <parameters-assignment-policy>

  5.             indexed

  6.         </parameters-assignment-policy>

  7.         <parameters>

  8.             <param name="sleepTime" value="14" />

  9.             <param name="x" value="1978" />

  10.             <param name="y" value="29" />

  11.         </parameters>

  12.         <parameters>

  13.             <param name="sleepTime" value="14" />

  14.             <param name="binding" value="glassfish-sum-soap-1.2" />

  15.             <param name="x" value="29" />

  16.             <param name="y" value="1978" />

  17.         </parameters>

  18.     </runner>


  19.     <bindings>

  20.         <binding uniqueName="jboss-sum-soap-1.2"

  21.                  serviceNameSpace="http://jee.test.jtr/"

  22.                  serviceName="ArithmeticalService"

  23.                  portName="ArithmeticalPort"

  24.                  operationName="sum"

  25.                  wsdlLoc="http://localhost:8080/jtrTestBench-jtrTestBench-ejb/Arithmetical?wsdl"

  26.                  serviceMode="PAYLOAD"

  27.                  jaxbObjectFactory="jtr.test.jee.arithmetical.client.ObjectFactory" />


  28.         <binding uniqueName="glassfish-sum-soap-1.2"

  29.                  serviceNameSpace="http://jee.test.jtr/"

  30.                  serviceName="ArithmeticalService"

  31.                  portName="ArithmeticalPort"

  32.                  operationName="sum"

  33.                  wsdlLoc="http://localhost:8181/ArithmeticalService/Arithmetical?wsdl"

  34.                  serviceMode="PAYLOAD"

  35.                  jaxbObjectFactory="jtr.test.jee.arithmetical.client.ObjectFactory" />

  36.     </bindings>

  37. </test>


  38. About the binding attribute

  39. Talking about the runner declaration, the only important thing to point-out is that we are using (both at the category-level and at instance-level in the second parameters-set) the binding attribute to declare that by default every WsArithmeticalTest instance must be fed with everything necessary for invoking the web-service uniquely identified by the jboss-sum-soap-1.2 name.

  40. Furthermore we say that all those instances that will be injected (fed) with the second parameters-set will be enabled to invoke the web-service uniquely identified by the glassfish-sum-soap-1.2 name.


  41. About the bindings and binding elements

  42. In order to leverage on the JTR-runtime capabilities for invoking web-services, you must define in the jtr.xml file the web-services you want to invoke. This can be done by means of the bindings element and its binding sub-element.




  43. The bindings element acts as a container of one or many binding elements, each describing a particular web-service operation to invoke.


  44. Each binding element requires the following attributes to properly describe a web-service operation to be invoked:

  45. uniqueName: the unique name that identifies this binding in the jtr.xml file and that will be used as an handle by those runners willing to invoke the described web-service

  46. serviceNameSpace: the targetNameSpace found in the WSDL

  47. serviceName: the service, present in the WSDL, that contains the operation to be invoked

  48. portName: the port exposed by the service, that contains the operation to be invoked

  49. operationName: the operation to be invoked

  50. wsdlLoc: where the WSDL can be found

  51. serviceMode: whether the endpoint accesses the message or the payload. Thus legal values are MESSAGE | PAYLOAD

  52. jaxbObjectFactory: the JAXB-generated class capable of producing input and output messages conforming with the grammar expressed in the WSDL


About using automatically generated clients

Typically there are many ways for invoking web-services. JTR leverages on DII (Dynamic Invocation Interface), but you could already have your somehow-generated clients or stubs and willing to use them

instead. Well, in such a case you can simply write a simple JSE runner (see here) that uses your clients or stubs to perform the actual web-service invocation.

Acting this way you get the best of both worlds: you continue using your clients/stubs but you can still take advantage of the JTR-runtime features such as parameterizations and stress-testing facilities, distributed testing and so on.




Next step: JUnit integration