7 Testing Web-Services
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:
•method to perform synchronous request-response web-services invocations
•method to perform synchronous one-way web-services invocations
•methods to perform asynchronous web-services invocations
•accessor to the current WS-binding associated with the runner instance
Here are the most relevant methods meant for performing web-services invocations, exposed by the AbstractWsRunner and by the jtr.runners.IRunnerWs interface:
/**
* Performs a synchronous invocation.
*
* @param binding The actual binding describing the web-services to invoke
* @param input The message
* @return The response
* @throws jtr.ws.WsProviderException
*/
public Object invoke(Binding binding, Object input) throws WsProviderException;
/**
* Performs a one-way webservice invocation.
*
* @param binding The actual binding describing the web-services to invoke
* @param input The message
* @throws jtr.ws.WsProviderException
*/
public void invokeOneWay(Binding binding, Object input) throws WsProviderException;
/**
* Performs an asynchronous invocation.
*
* @param binding The actual binding describing the web-service to invoke
* @param input The message
* @param rl The response listener to be notified when a response becomes available
* @return A reference to the <code>Future</code> instance representin the pending task
* @throws jtr.ws.WsProviderException
*/
public Future<?> invokeAsync(Binding binding, Object input, IWsResponseListener rl)
throws WsProviderException;
/**
* Performs an asynchronous invocation.
*
* @param binding The actual binding describing the web-service to invoke
* @param input The message
* @return A reference to the response one should poll on
* @throws jtr.ws.WsProviderException
*/
public IWsResponse invokeAsync(Binding binding, Object input) throws WsProviderException;
Web-Services invocations inputs & outputs
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:
xjc.sh -wsdl http://localhost:8080/jtrTestBench-jtrTestBench-ejb/Arithmetical?wsdl
produces an output like this:
parsing a schema...
compiling a schema...
jtr/test/jee/ObjectFactory.java
jtr/test/jee/Sum.java
jtr/test/jee/SumResponse.java
jtr/test/jee/package-info.java
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.
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.
Preparing the jtr.xml file
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:
<?xml version = '1.0' encoding = 'UTF-8'?>
<!-- JTRunner is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
-->
<test xmlns="http://jtrunner.sourceforge.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jtrunner.sourceforge.net file:./config/jtr.xsd"
runs="10">
<!-- JTR components factories -->
<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>
<runner runs="2" binding="jboss-sum-soap-1.2">
<runner-fqn>jtr.test.jee.client.WsArithmeticalTest</runner-fqn>
<instance-count>2</instance-count>
<parameters-assignment-policy>
indexed
</parameters-assignment-policy>
<parameters>
<param name="sleepTime" value="14" />
<param name="x" value="1978" />
<param name="y" value="29" />
</parameters>
<parameters>
<param name="sleepTime" value="14" />
<param name="binding" value="glassfish-sum-soap-1.2" />
<param name="x" value="29" />
<param name="y" value="1978" />
</parameters>
</runner>
<bindings>
<binding uniqueName="jboss-sum-soap-1.2"
serviceNameSpace="http://jee.test.jtr/"
serviceName="ArithmeticalService"
portName="ArithmeticalPort"
operationName="sum"
wsdlLoc="http://localhost:8080/jtrTestBench-jtrTestBench-ejb/Arithmetical?wsdl"
serviceMode="PAYLOAD"
jaxbObjectFactory="jtr.test.jee.arithmetical.client.ObjectFactory" />
<binding uniqueName="glassfish-sum-soap-1.2"
serviceNameSpace="http://jee.test.jtr/"
serviceName="ArithmeticalService"
portName="ArithmeticalPort"
operationName="sum"
wsdlLoc="http://localhost:8181/ArithmeticalService/Arithmetical?wsdl"
serviceMode="PAYLOAD"
jaxbObjectFactory="jtr.test.jee.arithmetical.client.ObjectFactory" />
</bindings>
</test>
About the binding attribute
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.
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.
About the bindings and binding elements
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.
The bindings element acts as a container of one or many binding elements, each describing a particular web-service operation to invoke.
Each binding element requires the following attributes to properly describe a web-service operation to be invoked:
•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
•serviceNameSpace: the targetNameSpace found in the WSDL
•serviceName: the service, present in the WSDL, that contains the operation to be invoked
•portName: the port exposed by the service, that contains the operation to be invoked
•operationName: the operation to be invoked
•wsdlLoc: where the WSDL can be found
•serviceMode: whether the endpoint accesses the message or the payload. Thus legal values are MESSAGE | PAYLOAD
•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.