MockWebServiceMessageSender is the entry-point to Spring WS Test. If it is used instead of standard WebServiceMessageSender all requests will be processed by the library.
Request processor is a basic building block of the library. There are two basic types of ReqestProcessors - request validators and response generators. This division is logical only. Both groups implement the same interface. When a request processor is called with a request, there are three possible outcomes.
Some parts of the library are complex. The complexity is not arbitrary, it's caused by the number of configuration settings. To make your life easier, most common configuration options are encapsulated in a custom tag library. For majority of scenarios provided custom tag library should suffice. The rest can use standard configuration which provides more flexibility.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:swst="http://javacrumbs.net/schema/1.0/spring-ws-test" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://javacrumbs.net/schema/1.0/spring-ws-test http://javacrumbs.net/schema/1.0/spring-ws-test.xsd"> <swst:mock-ws-message-sender id="mock-sender" autowireRequestProcessors="true" autoinjectMock="true"> <swst:namespaces> <entry key="ns" value="http://www.springframework.org/spring-ws/samples/airline/schemas/messages"/> </swst:namespaces> <swst:resource-config pathPrefix="mock-xml/" prependUri="false" templateProcessor="FreeMarker"> <swst:discriminators> <entry key="getFlightsRequest" value="//ns:from;//ns:to"/> </swst:discriminators> </swst:resource-config> <swst:schemas> xml/schema.xsd </swst:schemas> </swst:mock-ws-message-sender> </beans>
Schema based configuration are explained in the Quick start documentation. Here we will cover only advanced features.
In this part list of basic components and their configuration options will be described.
Resolves expressions. It's used in various parts of the library. Default implementation is XPathExpressionResolver. In order to use XPathExpression resolver, it's necessary to configure namespace prefixes.
<bean id="xpathResolver" class="net.javacrumbs.springws.test.expression.XPathExpressionResolver"> <property name="namespaceMap"> <map> <entry key="soapenv" value="http://schemas.xmlsoap.org/soap/envelope/" /> <entry key="ns" value="http://www.springframework.org/spring-ws/samples/airline/schemas/messages" /> </map> </property> </bean>
Resource lookup looks up a resource based on the request, uri and context attributes. It has two basic implementation.
Both variants require expression resolver to be set.
Both implementations also use a TemplateProcessor which processes a template. By default XsltTemplateProcessor is used, so a XSLT template can be used in all resources.
Generates mock response based on provided resource lookup. If the resource is not found, returns null. It's possible to include SOAP elements to the response but it's also possible to specify only payload. In this case SOAP envelope will be automatically generated.
<bean class="net.javacrumbs.springws.test.generator.DefaultResponseGenerator"> <property name="resourceLookup"> <bean class="net.javacrumbs.springws.test.lookup.ExpressionBasedResourceLookup"> <property name="resourceExpressions"> <list> <value>concat('mock-responses/',$uri.host, '/', local-name(//soapenv:Body/*[1]),'/',//ns:from,'-',//ns:to,'-response.xml')</value> <value>concat('mock-responses/',$uri.host, '/', local-name(//soapenv:Body/*[1]),'/default-response.xml')</value> </list> </property> <property name="expressionResolver" ref="xpathResolver"/> </bean> </property> </bean>
It's similar to the default request generator, the difference is that instead of generating responses it validates the request. You can use XSLT templates here as well. You can also specify special placeholder $IGNORE for values that should be ignored. It is also possible to define only payload in the control file. In such case only payloads will be compared and SOAP envelope will be ignored.
<bean class="net.javacrumbs.springws.test.validator.XmlCompareRequestValidator"> <property name="controlResourceLookup"> <bean class="net.javacrumbs.springws.test.lookup.ExpressionBasedResourceLookup"> <property name="resourceExpressions"> <list> <value>concat('mock-responses/',$uri.host, '/', local-name(//soapenv:Body/*[1]),'/',//ns:from,'-',//ns:to,'-request.xml')</value> </list> </property> </bean> </property> </bean>
It is also possible to validate request using XPath. For example you can use following configuration.
<bean class="net.javacrumbs.springws.test.validator.XPathRequestValidator"> <property name="expressionResolver" ref="xpathResolver"/> <property name="exceptionMapping"> <map> <entry key="//ns:serviceClass != 'economy' and //ns:serviceClass != 'business'" value="Unsupported service class"/> </map> </property> </bean>
Validator configured in this way will throw an exception if service class is not "economy" or "business".
Test context is useful, when you need to use some specific configuration. For example you want to set expected departure time and use it in the response.
WsTestContextHolder.getTestContext().setAttribute("departureTime", "2009-07-14");
You can set the attribute in the test method and then use it
It's a good idea to clear the test context at the tearDown() method.
In this part we will describe possibilities of the XPath evaluation.
Let's imagine that service URL is "http://www.csa.cz" and that following request was used
<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header /> <SOAP-ENV:Body> <ns3:GetFlightsRequest xmlns:ns2="http://www.springframework.org/spring-ws/samples/airline/schemas/types" xmlns:ns3="http://www.springframework.org/spring-ws/samples/airline/schemas/messages"> <ns3:from>PRG</ns3:from> <ns3:to>DUB</ns3:to> <ns3:departureDate>2059-07-27</ns3:departureDate> <ns3:serviceClass>economy</ns3:serviceClass> </ns3:GetFlightsRequest> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
Also assume that WsTestContext was set like this
WsTestContextHolder.getTestContext().setAttribute("testName", "test136");
Then following results will correspond to XPath expressions: XPathEval
XPath expression | Result |
---|---|
concat(local-name(//soapenv:Body/*[1]),'-response.xml') | GetFlightsRequest-response.xml |
concat(local-name(//soapenv:Body/*[1]),'/default-response.xml') | GetFlightsRequest/default-response.xml |
concat(local-name(//soapenv:Body/*[1]),'/',//ns:from,'-',//ns:to,'-response.xml') | GetFlightsRequest/PRG-DUB-response.xml |
concat($uri.host, '/', local-name(//soapenv:Body/*[1]),'/',//ns:from,'-',//ns:to,'-response.xml') | www.csa.cz/GetFlightsRequest/PRG-DUB-response.xml |
concat($context.testName,'-response.xml') | test136-response.xml |
Test context can be used for more fine grained test configuration.
In you test code you can specify a context attribute like this.
WsTestContextHolder.getTestContext().setAttribute("testName", "test136");
Later on you can either retrieve it programmatically like this
WsTestContextHolder.getTestContext().getAttribute("testName")
You can also access it in XPath like this
concat($context.testName,'-response.xml')
or it is automatically injected as XSLT variable, so it can be accessed like this
<?xml version="1.0" encoding="windows-1250"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:param name="number"/> <xsl:template match="/"> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Header /> <soapenv:Body> <ns:test xmlns:ns="http://www.example.org/schema"> <ns:number>2</ns:number> <ns:text><xsl:value-of select="$testName"/></ns:text> </ns:test> </soapenv:Body> </soapenv:Envelope> </xsl:template> </xsl:stylesheet>
Sometimes it useful to specify both requests and responses using templates. This way it's possible to use one file for multiple tests. By default XSLT template processor is used.
XSLT template uses the request as input. It's also possible to use context variables. A template can look like the following example. Please note how context variable is used for the departure time and value from the request for service class.
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:param name="departureTime"/> <xsl:template match="/"> <GetFlightsResponse xmlns="http://www.springframework.org/spring-ws/samples/airline/schemas/messages" xmlns:msg="http://www.springframework.org/spring-ws/samples/airline/schemas/messages" xmlns:tps="http://www.springframework.org/spring-ws/samples/airline/schemas/types"> <flight> <tps:number>OK0651</tps:number> <tps:departureTime><xsl:value-of select="$departureTime"/></tps:departureTime> <tps:from> <tps:code>PRG</tps:code> <tps:name>Prague</tps:name> <tps:city>Prague</tps:city> </tps:from> <tps:arrivalTime>2001-12-31T12:00:00 </tps:arrivalTime> <tps:to> <tps:code>CDG</tps:code> <tps:name>CDG</tps:name> <tps:city>Paris</tps:city> </tps:to> <tps:serviceClass><xsl:value-of select="//msg:serviceClass"/></tps:serviceClass> </flight> </GetFlightsResponse> </xsl:template> </xsl:stylesheet>
It's also possible to use a FreeMarker template. In this case you have to set "templateProcessor" attribute of "resource-config" element to "FreeMarker". Alternatively, it's possible to set templateProcessor property on resource lookup bean, if you are not using simplified configuration. A FreeMarker template could look like the following one.
<#ftl ns_prefixes={"msg":"http://www.springframework.org/spring-ws/samples/airline/schemas/messages"}> <GetFlightsResponse xmlns="http://www.springframework.org/spring-ws/samples/airline/schemas/messages" xmlns:msg="http://www.springframework.org/spring-ws/samples/airline/schemas/messages" xmlns:tps="http://www.springframework.org/spring-ws/samples/airline/schemas/types"> <flight> <tps:number>OK0651</tps:number> <tps:departureTime>${departureTime}</tps:departureTime> <tps:from> <tps:code>PRG</tps:code> <tps:name>Prague</tps:name> <tps:city>Prague</tps:city> </tps:from> <tps:arrivalTime>2001-12-31T12:00:00 </tps:arrivalTime> <tps:to> <tps:code>CDG</tps:code> <tps:name>CDG</tps:name> <tps:city>Paris</tps:city> </tps:to> <tps:serviceClass>${request["//msg:serviceClass"]}</tps:serviceClass> </flight> </GetFlightsResponse>